我就廢話不多說了,大家還是直接看代碼吧~
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 { //讀取結(jié)束,會報EOF
return result,nil
}
return nil,err
}
result = append(result,line)
}
return result,nil
}
補(bǔ)充:Golang讀取文件和處理超大文件方案
Golang 操作文件的讀取的方法很多,適用的場景也是各不相同,在此我們將文件的讀取分為如下幾種 :
文件整體讀取
文件分片讀取(塊級讀取)
文件行級讀取
系統(tǒng)的配置不同,執(zhí)行的耗時也不相同,此處給出一參考
系統(tǒng)配置 :
OS : Windows10
Memory : 16G
CPU (英特爾)Intel® Core™ i3-4370 CPU @ 3.80GHz(3800 MHz)
1. 文件整體讀取
文件整體讀取就是將文件一次性讀取到,理解上是將文件的內(nèi)容第一次就讀取完了
使用場景 :
針對小文件比較合適(大文件讀取空間和時間的消耗也很大)
對于整體性強(qiáng)的文件也比較合適(文件也不能太大)
代碼示例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
}
// 關(guān)閉文件
defer FileHandle.Close()
// 獲取文件當(dāng)前信息
fileInfo, err := FileHandle.Stat()
if err != nil {
log.Println(err)
return
}
buffer := make([]byte, fileInfo.Size())
// 讀取文件內(nèi)容,并寫入buffer中
n, err := FileHandle.Read(buffer)
if err != nil {
log.Println(err)
}
// 打印所有切片中的內(nèi)容
fmt.Println(string(buffer[:n]))
fmt.Println("readAllBuff spend : ", time.Now().Sub(start1))
}
func main() {
readAllBuff(m11)
readAllBuff(m400)
}
2. 文件分片讀取
對文件一部分一部分逐步的讀取,直到文件完全讀取完
PS : 每次讀取文件的大小是根據(jù)設(shè)置的 分片 大小 ,所以對于讀取文本類型的文件時(例如 : 日志文件)
不一定是按照你的期望逐行輸出,因為不會處理文本尾部的換行符,而是按照分片大小讀取內(nèi)容
使用場景 :
讀取超大的文件很合適
讀二進(jìn)制類型的文件很合適(比如:音視頻文件或者資源類型文件等)
代碼示例
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()
// 設(shè)置每次讀取字節(jié)數(shù)
buffer := make([]byte, 1024)
for {
n, err := FileHandle.Read(buffer)
// 控制條件,根據(jù)實際調(diào)整
if err != nil err != io.EOF {
log.Println(err)
}
if n == 0 {
break
}
// 如下代碼打印出每次讀取的文件塊(字節(jié)數(shù))
//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文件等)
讀取的文件最好是有換行的(如果使用單行文件組成的大文件,需要注意)
對需要分析內(nèi)容的大文件
統(tǒng)計某些數(shù)據(jù)出現(xiàn)的次數(shù)
查詢某些數(shù)據(jù)是否存在
查找指定行的數(shù)據(jù)
示例代碼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
}
// 如下是某些業(yè)務(wù)邏輯操作
// 如下代碼打印每次讀取的文件行內(nèi)容
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
// 實際邏輯 : 對讀取的內(nèi)容進(jìn)行某些業(yè)務(wù)操作
// 如下代碼打印每次讀取的文件行內(nèi)容
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. 總結(jié)
面試中常見的類似超大文件讀取的問題,通常我們采用分片讀取或者逐行讀取的方案即可
大文件的上傳也可以采用類似的解決方案 , 每次讀取文件的部分內(nèi)容上傳(寫入)網(wǎng)絡(luò)接口中,直至文件讀取完畢
普通的小文件并且對內(nèi)容沒有太多操作的,可以采用整體讀取,速度相對較快
對文件內(nèi)容有操作的采用分片讀取和逐行讀取更合適
二進(jìn)制類型文件采用分片讀取或者整體讀取的方案比較合適
文件讀取不僅是本地文件,要讀去網(wǎng)絡(luò)上的文件(各種文檔,音視頻,圖片,和其他各種類型文件)時要訪問到文件獲取 io.ReadCloser 或者 io.Reader 后可以采用三種方式將文件內(nèi)容讀取到
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 方法讀取
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- golang文件讀取-按指定BUFF大小讀取方式
- Golang 實現(xiàn)分片讀取http超大文件流和并發(fā)控制
- 淺談Golang是如何讀取文件內(nèi)容的(7種)
- golang 使用 viper 讀取自定義配置文件
- 如何利用Golang解析讀取Mysql備份文件
- golang讀取文件的常用方法總結(jié)
- Golang 實現(xiàn)超大文件讀取的兩種方法