当前位置: 首页 > 科技观察

超全总结:Go中读取文件的10种方法

时间:2023-03-21 14:46:19 科技观察

大家好,我是明哥。Go中读写文件内容的方式有很多种,大部分都是基于syscall或者os库的高层封装。不同的库有不同的适用场景。花了一些时间来解决这个问题。这是第一篇,先介绍10种读取文件的方法,过两天再介绍写文件的方法。1、将整个文件读入内存直接将数据读入内存,这是最高效的方法,但是这种方法只适用于小文件,不适合大文件,因为很浪费内存。1.1直接指定文件名读取有两种方法第一种:使用os.ReadFilepackagemainimport("fmt""os")funcmain(){content,err:=os.ReadFile("a.txt")iferr!=nil{panic(err)}fmt.Println(string(content))}第二:使用ioutil.ReadFilepackagemainimport("io/ioutil""fmt")funcmain(){content,err:=ioutil.ReadFile("a.txt")iferr!=nil{panic(err)}fmt.Println(string(content))其实从Go1.16开始,ioutil.ReadFile相当于os.ReadFile,两者完全一致//ReadFilereadsthefilenamedbyfilenameandreturnsthecontents.//Asuccessfulcallreturnsserr==nil,noterr==EOF。因为ReadFile//读取了整个文件,它不会将ReadFile中的EOF当作一个错误//报告。////AsofGo1.16,这个函数只是调用sos.ReadFile.funcReadFile(filenamestring)([]byte,error){returnos.ReadFile()}1.2先创建句柄再读取。如果只是为了阅读,可以使用高级函数os.Openpackagemainimport("os""io/ioutil""fmt")funcmain(){file,err:=os.Open("a.txt")iferr!=nil{panic(err)}deferfile.Close()content,err:=ioutil.ReadAll(file)fmt.Println(string(content))据说是高级函数,因为它是只读模式*PathError.funcOpen(namestring)(*File,error){returnOpenFile(name,O_RDONLY,0)}所以也可以直接使用os.OpenFile,只需要多加两个参数packagemainimport("fmt""io/ioutil""os")funcmain(){file,err:=os.OpenFile("a.txt",os.O_RDONLY,0)iferr!=nil{panic(err)}deferfile.Close()content,err:=ioutil.ReadAll(file)fmt.Println(string(content))}2.一次只读一行,一次读取所有数据,太耗内存,可以指定一次只读一行。一共有三个方法:bufio.ReadLine()bufio.ReadBytes('\n')bufio.ReadString('\n')在bufio的源码注释中说bufio.ReadLine()是一个底层库,不适合普通用户,推荐用户使用bufio.ReadBytes和bufio.ReadString读取单行数据。所以这里不再介绍bufio.ReadLine()。2.1使用bufio.ReadBytespackagemainimport("bufio""fmt""io""os""strings")funcmain(){//创建句柄fi,err:=os.Open("christmas_apple.py")iferr!=nil{panic(err)}//创建Readerr:=bufio.NewReader(fi)for{lineBytes,err:=r.ReadBytes('\n')line:=strings.TrimSpace(string(lineBytes))iferr!=nil&&err!=io.EOF{panic(err)}iferr==io.EOF{break}fmt.Println(line)}}2.2使用bufio.ReadStringpackagemainimport("bufio""fmt""io""os""strings")funcmain(){//创建句柄fi,err:=os.Open("a.txt")iferr!=nil{panic(err)}//创建Readerr:=bufio.NewReader(fi)for{line,err:=r.ReadString('\n')line=strings.TrimSpace(line)iferr!=nil&&err!=io.EOF{panic(err)}iferr==io.EOF{break}fmt.Println(line)}}3、每次只读固定字节数每次只读一行数据,可以解决内存占用过大的问题,但需要注意的是,并不是所有的文件都带有换行符\n。所以,对于一些没有换行的大文件,就得想别的办法了。3.1os库的一般使用方法是:先创建一个文件句柄,可以使用os.Open或os.OpenFile再用bufio.NewReader创建一个Reader然后在for循环中调用Reader的Read函数,并且只每次读取固定字节数的数据。packagemainimport("bufio""fmt""io""os")funcmain(){//创建句柄fi,err:=os.Open("a.txt")iferr!=nil{panic(err)}//CreateReaderr:=bufio.NewReader(fi)//每次读取1024字节buf:=make([]byte,1024)for{n,err:=r.Read(buf)iferr!=nil&&err!=io.EOF{panic(err)}ifn==0{break}fmt.Println(string(buf[:n]))}}3.2使用syscall库os库本质上调用的是syscall库,但是由于syscall太low-level,如果没有特殊需要,一般不用syscall。为了内容的完整性,这里也以syscall为例。本例中每次会读取100个字节的数据发送到通道,由另一个协程读取并打印。packagemainimport("fmt""sync""syscall")funcmain(){fd,err:=syscall.Open("christmas_apple.py",syscall.O_RDONLY,0)iferr!=nil{fmt.Println("Failedonopen:",err)}defersyscall.Close(fd)varwgsync.WaitGroupwg.Add(2)dataChan:=make(chan[]byte)gofunc(){wg.Done()for{data:=make([]byte,100)n,_:=syscall.Read(fd,data)ifn==0{break}dataChan<-data}close(dataChan)}()gofunc(){deferwg.Done()for{select{casedata,ok:=<-dataChan:if!ok{return}fmt.Printf(string(data))default:}}}()wg.Wait()}代码注意。转载本文请联系围棋编程时间公众号。