golang逐行读取文件的操作

 更新时间:2020年12月22日 10:18:39   作者:思维的深度  
这篇文章主要介绍了golang逐行读取文件的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

我就废话不多说了,大家还是直接看代码吧~

func ReadLine(fileName string) ([]string,error){
	f, err := os.Open(fileName)
	if err != nil {
		return nil,err
	}
	buf := bufio.NewReader(f)
	var result []string
	for {
		line, err := buf.ReadString('\n')
		line = strings.TrimSpace(line)
		if err != nil {
			if err == io.EOF { //读取结束,会报EOF
				return result,nil
			}
			return nil,err
		}
		result = append(result,line)
	}
	return result,nil
}

补充:Golang读取文件和处理超大文件方案

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

文件整体读取

文件分片读取(块级读取)

文件行级读取

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

系统配置 :

OS : Windows10

Memory : 16G

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

1. 文件整体读取

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

使用场景 :

针对小文件比较合适(大文件读取空间和时间的消耗也很大)

对于整体性强的文件也比较合适(文件也不能太大)

代码示例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. 文件分片读取

对文件一部分一部分逐步的读取,直到文件完全读取完

PS : 每次读取文件的大小是根据设置的 分片 大小 ,所以对于读取文本类型的文件时(例如 : 日志文件)

不一定是按照你的期望逐行输出,因为不会处理文本尾部的换行符,而是按照分片大小读取内容

使用场景 :

读取超大的文件很合适

读二进制类型的文件很合适(比如:音视频文件或者资源类型文件等)

代码示例

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. 文件逐行读取

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

使用场景 :

读取超大的文件很合适(例如 : 超大log文件等)

读取的文件最好是有换行的(如果使用单行文件组成的大文件,需要注意)

对需要分析内容的大文件

统计某些数据出现的次数

查询某些数据是否存在

查找指定行的数据

示例代码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. 总结

面试中常见的类似超大文件读取的问题,通常我们采用分片读取或者逐行读取的方案即可

大文件的上传也可以采用类似的解决方案 , 每次读取文件的部分内容上传(写入)网络接口中,直至文件读取完毕

普通的小文件并且对内容没有太多操作的,可以采用整体读取,速度相对较快

对文件内容有操作的采用分片读取和逐行读取更合适

二进制类型文件采用分片读取或者整体读取的方案比较合适

文件读取不仅是本地文件,要读去网络上的文件(各种文档,音视频,图片,和其他各种类型文件)时要访问到文件获取 io.ReadCloser 或者 io.Reader 后可以采用三种方式将文件内容读取到

func ReadAll(r io.Reader) ([]byte, error) 文件完整读取

func Copy(dst Writer, src Reader) (written int64, err error) 文件读取并写入

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

通过Reader 接口的 Read 方法读取

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

相关文章

  • Go语言中的自定义函数类型的实现

    Go语言中的自定义函数类型的实现

    在Go语言中,函数类型是一种将函数作为值的数据类型,本文主要介绍了Go语言中的自定义函数类型,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • Golang如何实现任意进制转换的方法示例

    Golang如何实现任意进制转换的方法示例

    进制转换是人们利用符号来计数的方法,进制转换由一组数码符号和两个基本因素“基数”与“位权”构成,这篇文章主要给大家介绍了关于Golang如何实现10进制转换62进制的方法,文中给出了详细的示例代码供大家参考学习学习,下面随着小编来一起学习学习吧。
    2017-09-09
  • golang中json小谈之字符串转浮点数的操作

    golang中json小谈之字符串转浮点数的操作

    这篇文章主要介绍了golang中json小谈之字符串转浮点数的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • golang中的string与其他格式数据的转换方法详解

    golang中的string与其他格式数据的转换方法详解

    这篇文章主要介绍了golang中的string与其他格式数据的转换方法,文章通过代码示例介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-10-10
  • 在go中使用omitempty的代码实例

    在go中使用omitempty的代码实例

    今天小编就为大家分享一篇关于在go中使用omitempty的代码实例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • golang中json操作的完全指南

    golang中json操作的完全指南

    golang标准库有一个名叫encoding/json的包,包含了JSON的序列化(Marshal)和反序列化(Unmarshal)的能力,下面这篇文章主要给大家介绍了关于golang中json操作的相关资料,需要的朋友可以参考下
    2022-07-07
  • golang使用go mod导入本地包和第三方包的方式

    golang使用go mod导入本地包和第三方包的方式

    这篇文章主要介绍了golang使用go mod导入本地包和第三方包的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Go语言实现常用排序算法的示例代码

    Go语言实现常用排序算法的示例代码

    排序算法是在生活中随处可见,也是算法基础,因为其实现代码较短,应用较常见。所以在面试中经常会问到排序算法及其相关的问题。本文为大家整理了一些Go语言中常用排序算法的实现,需要的可以参考一下
    2022-08-08
  • 基于go微服务效率工具goctl深度解析

    基于go微服务效率工具goctl深度解析

    这篇文章主要为大家介绍了基于go微服务效率工具goctl深度解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • go实现冒泡排序算法

    go实现冒泡排序算法

    冒泡排序算法是数据结构中常用的一种算法,本文就介绍了go实现冒泡排序算法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论