1、ioutil读取整个文件(io/ioutil)
ioutil的方式能够读取整个文件, 只需传入文件名, 操作简单。该操作方式需要把文件读入内容, 效率高, 同样占用内存也高
func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件, 它不会将读取返回的EOF视为应报告的错误。// ioutil.ReadFile读取整个文件
func main() {content, err := ioutil.ReadFile("./main.go")if err != nil {fmt.Println("read file failed, err:", err)return}fmt.Println(string(content))}
func ReadAll(r io.Reader) ([]byte, error)
ReadAll与ReadFile的区别在于传入的参数类型不同, 该方法需要传入一个io.Reader.ReadAll从r读取数据直到EOF或遇到error, 返回读取的数据和遇到的错误。
成功的调用返回的err为nil而非EOF。因为本函数定义为读取r直到EOF, 它不会将读取返回的EOF视为应报告的错误。
// ioutil.ReadAll读取整个文件func main() { file,err := os.Open("./main.go") if err != nil { fmt.Println(err) return } defer file.Close() content, err := ioutil.ReadAll(file) if err != nil { fmt.Println("read file failed, err:", err) return } fmt.Println(string(content))}
2、File.Read(属于os包)
func (f *File) Read(b []byte) (n int, err error)
Read方法从f中读取最多len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。
func main() { file, err := os.Open("test.txt") // For read access. if err != nil { log.Fatal(err) } defer file.Close() var content []byte var tmp = make([]byte, 128) // 一次读取多少个字节 for { n, err := file.Read(tmp) if err == io.EOF { break } if err != nil { fmt.Println("read file failed, err:", err) return } content = append(content, tmp[:n]...) } fmt.Println(string(content))}
3、bufio读取文件(bufio包)
该操作同样可看作按行读取, 将数据读入缓冲区
func (b *Reader) ReadString(delim byte) (line string, err error)
ReadString读取直到第一次遇到delim字节, 返回一个包含已读取的数据和delim字节的字符串。如果ReadString方法在读取到delim之前遇到了错误, 它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。
当且仅当ReadString方法返回的切片不以delim结尾时, 会返回一个非nil的错误。
// bufio按行读取示例func main() { file, err := os.Open("./test.txt") if err != nil { fmt.Println("open file failed, err:", err) return } defer file.Close() var content string reader := bufio.NewReader(file) for { line, err := reader.ReadString('\n') if err != nil { if err == io.EOF { break } fmt.Println("read file failed, err:", err) return } content += line } fmt.Println(content)}
另外一种方式为ReadBytes, 与readString的区别为返回格式为切片类型
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
ReadBytes读取直到第一次遇到delim字节, 返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误, 它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。
当且仅当ReadBytes方法返回的切片不以delim结尾时, 会返回一个非nil的错误。
// bufio按行读取示例func main() { file, err := os.Open("./test.txt") if err != nil { fmt.Println("open file failed, err:", err) return } defer file.Close() var content []byte reader := bufio.NewReader(file) for { line, err := reader.ReadBytes('\n') if err != nil { if err == io.EOF { break } fmt.Println("read file failed, err:", err) return } content = append(content,line...) } fmt.Println(string(content))}
func NewScanner(r io.Reader) *Scanner
NewScanner函数用于初始化一个Scanner对象, NewScanner返回一个新的Scanner来扫描r, 默认匹配函数为ScanLines(行匹配函数)
package mainimport ( "bufio" "fmt" "os")func main() { file, err := os.Open("abc.txt") if err != nil { panic(err) } scanner := bufio.NewScanner(file) for scanner.Scan() { fmt.Println(scanner.Text()) }}
结论:
当文件较小(KB 级别)时, ioutil > bufio > os。
当文件大小比较常规(MB 级别)时, 三者差别不大, 但 bufio 又是已经显现出来。
当文件较大(GB 级别)时, bufio > os > ioutil。
注意: ioutil.ReadAll(data) // io.Reader 带缓冲区(512)的读取
汇总: 效率高的读写方式
package mainimport ( "bufio" "fmt" "io" "io/ioutil" "os")// 小文件读取方式// 第一种 ioutil.ReadFile 读取小文件的方式 效率高的方式func ReadFile(path string) (buf []byte, err error) { buf, err = ioutil.ReadFile(path) if err != nil { return []byte{}, err } return buf, nil}// 第二种 ioutil.ReadAll 读取小文件的方式func ReadAll(path string) (buf []byte, err error) { f, err := os.Open(path) if err != nil { return []byte{}, err } defer f.Close() buf, err = ioutil.ReadAll(f) if err != nil { return []byte{}, err } return buf, nil}// 超大文件读取方式// 第一种方式 Read方式 分片读取 读取超大文件 (读取的是二进制文件, 没有换行符的时候)func ReadBuf(path string) (buf []byte, err error) { f, err := os.Open(path) if err != nil { return []byte{}, err } defer f.Close() buf = make([]byte, 4096) reader := bufio.NewReader(f) // 设置缓冲区 // 参照标准包的 ReadFile 函数 var size int if info, err := f.Stat(); err == nil { size64 := info.Size() if int64(int(size64)) == size64 { size = int(size64) } } size++ // one byte for final read at EOF if size < 512 { size = 512 } data := make([]byte, size) // 初始化一个切片, 容量大小为文件的大小 for { n, err := reader.Read(buf[:]) // 这里传递 读取片段 if err != nil { // 遇到任何错误立即返回,并忽略 EOF 错误信息 if err == io.EOF { break } break } data = append(data, buf[:n]...) } return data, nil}// 第二种方式 逐行读取 ReadLine 方式 读取超大的方式 文件中有明确的换行符/*注意: 不同的操作系统换行符不一样, 测试的结果误差相差很大// windows平台相当于 "\r\n";// unix\linux平台相当于 "\n";// mac平台相当于 "\r";*/func ReadLine(path string) (buf []byte, err error) { f, err := os.Open(path) if err != nil { return []byte{}, err } defer f.Close() // 参照标准包的 ReadFile 函数 var size int if info, err := f.Stat(); err == nil { size64 := info.Size() if int64(int(size64)) == size64 { size = int(size64) } } size++ // one byte for final read at EOF if size < 512 { size = 512 } data := make([]byte, size) // 根据文件的大小设置切片的容量 r := bufio.NewReader(f) // 设置缓冲区 for { // 相对于ReadString(或ReadBytes) 函数, ReadLine 读取文件更快, 原因是由于 ReadString 后端调用 ReadBytes, 而 ReadBytes 多次使用 copy 方法造成大量耗时。 // line, err := r.ReadBytes('\n') // 返回值: []byte line, err := r.ReadString('\n') // 返回值: string line, _, err := r.ReadLine() if err != nil { if err == io.EOF { // 读取到文件结尾 break } // log.Println("read file failed, err:", err) break // break 跳出 for 循环, 也可以使用 return, 但是不容易理解 } data = append(data, line...) } return data, nil}func main() { /* //小文件读取方式 自带的系统函数直接读取 buf, err := ioutil.ReadFile(path) if err != nil { fmt.Println(err) } fmt.Println(string(buf)) */ /* data, _ := ReadBuf("version.txt") fmt.Println(string(data)) */ buf, err := ReadLine("version.txt") if err != nil { fmt.Println(err) } fmt.Println(string(buf))}
结论: 小文件读取的方式: 使用自带的 ioutil.ReadAll 方法效率最高, 分析其源代码其本质上是 f.Read 的读取方式, 但是没有使用 bufio 缓冲区;
大文件读取的方式: 使用分块和逐行读取方式, 一般而言采用分块读取的方式, 而逐行读取的方式适用于日志文件(有换行符);
小文件的读取不必使用 bufio 缓冲区;