在Go读取文件返回字节流,不将所有内容自动转换为字符串的好处是可以避免昂贵的字符串分配,避免增加GC压力。
为了使本文更加简单,我将使用string(arrayOfBytes)将bytes数组转换为字符串。 但是,在发布生产代码时一般不建议这么做。
1.读取整个文件到内存中
我们从os软件包中提供的os.open()开始。需知道:
- 该文件必须容纳在内存中
- 我们需要预先知道文件的大小,以便实例化一个足以容纳它的缓冲区。
有了os.File对象的句柄,我们可以查询大小并实例化一个字节列表。
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("./entrypoint.sh") //(开门)
if err != nil {
fmt.Println(err)
return
}
defer file.Close() //(关门)
fileInfo, err := file.Stat() //获取文件属性
if err != nil {
fmt.Println(err)
return
}
fileSize := fileInfo.Size() //文件大小
buffer := make([]byte, fileSize) //设置一个byte的数组(buffer)
n, err := file.Read(buffer) //读文件(拿取)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("字节数:", n)
fmt.Println("bytestream to string:\n", string(buffer))
}
FileInfo提供的接口
type FileInfo interface {
Name() string // 文件的名字(不含扩展名)
Size() int64 // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同
Mode() FileMode // 文件的模式位
ModTime() time.Time // 文件的修改时间
IsDir() bool // 等价于Mode().IsDir()
Sys() interface{} // 底层数据来源(可以返回nil)
2.块读取
- 文件太大,可以分多次读取
package main
import (
"fmt"
"io"
"os"
)
const BufferSize = 100 //块的读取数据
func main() {
file, err := os.Open("./entrypoint.sh")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
buffer := make([]byte, BufferSize)
for {
n, err := file.Read(buffer)
if err != nil {
if err != io.EOF {
fmt.Println(err)
}
break
}
fmt.Println("字节数:", n)
fmt.Println("bytestream to string:\n", string(buffer[:n]))
}
}
3.ioutil
功能:
- 将文件读入缓冲区
- 简单了
func main() {
bytes, err := ioutil.ReadFile("./entrypoint.sh") //返回[]byte
if err != nil {
return
}
fmt.Println("字节数:", len(bytes))
fmt.Println("String read:\n", string(bytes))
}
4.ioutil.ReadDir
功能
- 读取文件的整个目录
- 然后读取整个文件
import (
"fmt"
"io/ioutil"
)
func main() {
fileList, err := ioutil.ReadDir("cmd")
if err != nil {
return
}
for _, fileInfo := range fileList {
if fileInfo.Mode().IsRegular() {
fmt.Println(fileInfo.Name())
bytes, err := ioutil.ReadFile(fileInfo.Name())
if err != nil {
fmt.Println("err:", err)
return
}
fmt.Println("字节数: ", len(bytes))
fmt.Println("String read:\n", string(bytes))
}
}
}