go按行讀取文件的三種實(shí)現(xiàn)方式匯總
1. 使用ioutil讀取文本
// 全部讀取后按換行拆分
func ReadFile1(path string) error {
fileHanle,err := os.OpenFile(path, os.O_RDONLY, 0666)
if err != nil {
return err
}
defer fileHanle.Close()
readBytes, err := ioutil.ReadAll(fileHanle)
if err != nil {
return err
}
results := strings.Split(string(readBytes), "\n")
fmt.Printf("read result:%v", results)
return nil
}實(shí)現(xiàn)方式:使用iouitl一次性讀取全部文件內(nèi)容,然后使用"\n"進(jìn)行分割成行。
這種實(shí)現(xiàn)最簡(jiǎn)單,但是只適合都內(nèi)容比較小的文件,當(dāng)讀取大文件的時(shí)候,一次讀到內(nèi)存需要占用比較大的內(nèi)存。
2. 使用bufio.Reader的ReadLine讀取
func ReadFile2(path string) error {
fileHanle,err := os.OpenFile(path, os.O_RDONLY, 0666)
if err != nil {
return err
}
defer fileHanle.Close()
reader := bufio.NewReader(fileHanle)
var results []string
// 按行處理txt
for {
line, _, err := reader.ReadLine()
if err == io.EOF {
break
}
results = append(results, string(line))
}
fmt.Printf("read result:%v\n", results)
return nil
}實(shí)現(xiàn)方式:使用NewReader創(chuàng)建bufio.Reader,循環(huán)調(diào)用Reader的ReadLine按行讀取,直接讀到文件結(jié)束標(biāo)記EOF。
bufio.Reader封裝了io, 并實(shí)現(xiàn)了緩沖I/O,同時(shí)它也實(shí)現(xiàn)了io.Reader的方法的Read方法。bufio緩沖區(qū)有默認(rèn)大小是4K。
從ReadLine返回的文本不包括行尾(“\r\n”或“\n”)。
如果一行大于緩存,isPrefix 會(huì)被設(shè)置為 true,同時(shí)返回該行的開始部分(等于緩存大小的部分)。該行剩余的部分就會(huì)在下次調(diào)用的時(shí)候返回。當(dāng)下次調(diào)用返回該行剩余部分時(shí),isPrefix 將會(huì)是 false 。
bufio.Reader的ReadLine最終調(diào)用的是ReadSlice方法,而ReadSlice返回的[]byte是指向Reader 中的buffer的一個(gè)slice,而不是copy一份返回,所以讀取的slice可能會(huì)被一下讀取操作重新,所以官方建議是使用ReadBytes和ReadString方法。
要注意是ReadBytes和ReadString返回的結(jié)果中包含傳入的界定符,如果最終結(jié)果不需要界定符的話需要自己處理。
bufio.Reader除了有ReadLine按行讀取外,他還封裝了按指定標(biāo)記分割的方法。如下圖

3.使用bufio.Scanner讀取
func ReadFile3(path string) error {
fileHanle,err := os.OpenFile(path, os.O_RDONLY, 0666)
if err != nil {
return err
}
defer fileHanle.Close()
scanner := bufio.NewScanner(fileHanle)
var results []string
// 按行處理txt
for scanner.Scan(){
lineTxt := strings.TrimSpace(scanner.Text())
if len(lineTxt) == 0 {
continue
}
results = append(results, lineTxt)
}
fmt.Printf("read result:%v\n", results)
return nil
}實(shí)現(xiàn)方式:使用NewScanner創(chuàng)建bufio.Scanner,使用循環(huán)調(diào)用scanner的Scan判斷是否掃描到數(shù)據(jù),然后通過(guò)scannner.Text()方法獲取到掃描的字符串。
bufio.Scanner它底層封裝了io.Reader, 它的實(shí)現(xiàn)就跟Scanner名稱一樣,是一個(gè)按字節(jié)流掃描的掃描器,當(dāng)掃描到滿足Split函數(shù)條件的字節(jié)數(shù)據(jù)后,就直接返回對(duì)應(yīng)的掃描到的內(nèi)容。
默認(rèn)情況下,它64k行限制,如果想更大,可以自己通過(guò)Buffer函數(shù)進(jìn)行設(shè)置。
Scanner默認(rèn)提供了以下方法:

Scanner 類型具有 Split 函數(shù),該函數(shù)接受 SplitFunc 函數(shù)來(lái)確定 Scanner 如何拆分給定的字節(jié)片。默認(rèn)的 SplitFunc 是 ScanLines,它將返回文本的每一行,并刪除行尾標(biāo)記。Split的函數(shù)定義如下:
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
我們可以自定義實(shí)現(xiàn)SpiteFunc來(lái)實(shí)現(xiàn)不同的拆分方式,比如我們可以使用bufio.ScanWords實(shí)現(xiàn)方式來(lái)按單詞拆分,如下:
func WordCounter(){
const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(bufio.ScanWords)
count := 0
for scanner.Scan() {
count++
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
fmt.Printf("%d\n", count)
}我可以跟蹤到bufio包scan.go的文件可以看到ScanWords的實(shí)現(xiàn)代碼如下:
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
// Skip leading spaces.
start := 0
for width := 0; start < len(data); start += width {
var r rune
r, width = utf8.DecodeRune(data[start:])
if !isSpace(r) {
break
}
}
// Scan until space, marking end of word.
for width, i := 0, start; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
if isSpace(r) {
return i + width, data[start:i], nil
}
}
// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
if atEOF && len(data) > start {
return len(data), data[start:], nil
}
// Request more data.
return start, nil, nil
}該函數(shù)簽名和SplitFunc定義實(shí)現(xiàn)一致。
總結(jié)
到此這篇關(guān)于go按行讀取文件的三種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)go按行讀取文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang如何通過(guò)viper讀取config.yaml文件
這篇文章主要介紹了golang通過(guò)viper讀取config.yaml文件,圍繞golang讀取config.yaml文件的相關(guān)資料展開詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-03-03
淺談go-restful框架的使用和實(shí)現(xiàn)
這篇文章主要介紹了淺談go-restful框架的使用和實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
go 實(shí)現(xiàn)簡(jiǎn)易端口掃描的示例
該功能實(shí)現(xiàn)原理很簡(jiǎn)單,就是發(fā)送socket連接(IP+端口),如果能連接成功,說(shuō)明目標(biāo)主機(jī)開放了某端口。當(dāng)要大量掃描端口時(shí),就需要寫并發(fā)編程了。2021-05-05
GoLang strings.Builder底層實(shí)現(xiàn)方法詳解
自從學(xué)習(xí)go一個(gè)月以來(lái),我多少使用了一下strings.Builder,略有心得。你也許知道它,特別是你了解bytes.Buffer的話。所以我在此分享一下我的心得,并希望能對(duì)你有所幫助2022-10-10

