文章目录

Golang 操作文件的读取的方法很多,适用的场景也是各不相同,在此我们将文件的读取分为如下几种 :

  1. 文件整体读取
  2. 文件分片读取(块级读取)
  3. 文件行级读取

系统的配置不同,执行的耗时也不相同,此处给出一参考

系统配置 :

OS : Windows10

Memory : 16G

CPU (英特尔)Intel® Core™ i3-4370 CPU @ 3.80GHz(3800 MHz)

1. 文件整体读取

文件整体读取就是将文件一次性读取到,理解上是将文件的内容第一次就读取完了

使用场景 :

  1. 针对小文件比较合适(大文件读取空间和时间的消耗也很大)
  2. 对于整体性强的文件也比较合适(文件也不能太大)
代码示例1
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`

// 将整个文件都读取
func readAll(filePath string) {
	start1 := time.Now()
	ioutil.ReadFile(filePath)
	fmt.Println("readAll spend : ", time.Now().Sub(start1))
}
func main() {
	readAll(m11)
	readAll(m400)
}
$ go run main.go
readAll spend :  6.9999ms
readAll spend :  358.8014ms
代码示例2
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 将文件完整读取
func readAllBuff(filePath string) {
	start1 := time.Now()
	// 打开文件
	FileHandle, err := os.Open(filePath)
	if err != nil {
		log.Println(err)
		return
	}
	// 关闭文件
	defer FileHandle.Close()
	// 获取文件当前信息
	fileInfo, err := FileHandle.Stat()
	if err != nil {
		log.Println(err)
		return
	}
	buffer := make([]byte, fileInfo.Size())
	// 读取文件内容,并写入buffer中
	n, err := FileHandle.Read(buffer)
	if err != nil {
		log.Println(err)
	}
	// 打印所有切片中的内容
	fmt.Println(string(buffer[:n]))
	fmt.Println("readAllBuff spend : ", time.Now().Sub(start1))
}
func main() {
	readAllBuff(m11)
	readAllBuff(m400)
}

2. 文件分片读取

分片
代码示例
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 文件一块一块的读取
func readBlock(filePath string) {
	start1 := time.Now()
	FileHandle, err := os.Open(filePath)
	if err != nil {
		log.Println(err)
		return
	}
	defer FileHandle.Close()
    // 设置每次读取字节数
	buffer := make([]byte, 1024)
	for {
		n, err := FileHandle.Read(buffer)
		// 控制条件,根据实际调整
		if err != nil && err != io.EOF {
			log.Println(err)
		}
		if n == 0 {
			break
		}
		// 如下代码打印出每次读取的文件块(字节数)
		//fmt.Println(string(buffer[:n]))
	}
	fmt.Println("readBolck spend : ", time.Now().Sub(start1))
}
func main() {
	readBlock(m11)
	readBlock(m400)
}
$ go run main.go
readBolck spend :  31.9814ms
readBolck spend :  1.0889488s

3. 文件逐行读取

对文件一行一行的读取,直到读到文件末尾

使用场景 :

  1. 读取超大的文件很合适(例如 : 超大log文件等)
  2. 读取的文件最好是有换行的(如果使用单行文件组成的大文件,需要注意)
  3. 对需要分析内容的大文件
    1. 统计某些数据出现的次数
    2. 查询某些数据是否存在
    3. 查找指定行的数据
示例代码1
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 读取文件的每一行
func readEachLineReader(filePath string) {
	start1 := time.Now()
	FileHandle, err := os.Open(filePath)
	if err != nil {
		log.Println(err)
		return
	}
	defer FileHandle.Close()
	lineReader := bufio.NewReader(FileHandle)
	for {
        // 相同使用场景下可以采用的方法
		// func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
		// func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
		// func (b *Reader) ReadString(delim byte) (line string, err error)
		line, _, err := lineReader.ReadLine()
		if err == io.EOF {
			break
		}
		// 如下是某些业务逻辑操作
		// 如下代码打印每次读取的文件行内容
		fmt.Println(string(line))
	}
	fmt.Println("readEachLineReader spend : ", time.Now().Sub(start1))
}
func main(){
    readEachLineReader(m11)
	readEachLineReader(m400)
}
$ go run main.go
readEachLineReader spend :  16.9902ms
readEachLineReader spend :  537.9683ms

代码示例2
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"
)
// 测试用的文本文件11M大小
var m11 string = `G:\runtime\log\ccapi\11M.log`
// 测试用的文本文件400M大小
var m400 string = `G:\runtime\log\ccapi\400M.log`
// 读取文件的每一行
func readEachLineScanner(filePath string) {
	start1 := time.Now()
	FileHandle, err := os.Open(filePath)
	if err != nil {
		log.Println(err)
		return
	}
	defer FileHandle.Close()
	lineScanner := bufio.NewScanner(FileHandle)
	for lineScanner.Scan() {
        // 相同使用场景下可以使用如下方法
		// func (s *Scanner) Bytes() []byte
		// func (s *Scanner) Text() string
		// 实际逻辑 : 对读取的内容进行某些业务操作
		// 如下代码打印每次读取的文件行内容
		fmt.Println(lineScanner.Text())
	}
	fmt.Println("readEachLineScanner spend : ", time.Now().Sub(start1))
}
func main() {
	readEachLineScanner(m11)
	readEachLineScanner(m400)
}
$ go run main.go
readEachLineScanner spend :  17.9895ms
readEachLineScanner spend :  574.1722ms

4. 总结

type Reader interface {
    Read(p []byte) (n int, err error)
}

有疏漏 , 万望指正 , 不胜感谢

5. 参考资料