欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang如何讀取單行超長(zhǎng)的文本詳解

 更新時(shí)間:2021年12月22日 09:13:39   作者:CrazyDragon_King  
這篇文章主要給大家介紹了關(guān)于Golang如何讀取單行超長(zhǎng)文本的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言:

最近在探索用Go來(lái)讀取文件,讀取文本時(shí)發(fā)現(xiàn),對(duì)于單行超長(zhǎng)的文本,我的Go代碼無(wú)法處理。經(jīng)過(guò)查閱才發(fā)現(xiàn),Go提供的Scanner無(wú)法讀取單行超長(zhǎng)文本文件。我這里就來(lái)總結(jié)一下問(wèn)題的發(fā)現(xiàn)和解決過(guò)程。

1.問(wèn)題復(fù)現(xiàn)

首先注釋main函數(shù)里面的內(nèi)容,執(zhí)行 CreateBigText 函數(shù),它會(huì)創(chuàng)建一個(gè)含有3行內(nèi)容的文件,第一行是一個(gè)長(zhǎng)度超過(guò)100KB的行。然后解決main函數(shù)的注釋?zhuān)瑖L試執(zhí)行代碼,會(huì)發(fā)現(xiàn)只有一行錯(cuò)誤信息:

package main

import (
	"bufio"
	"bytes"
	"log"
	"os"
	"strconv"
)

func main() {
	file, err := os.Open("./read/test.txt")
	if err != nil {
		log.Fatal(err)
	}
	ReadBigText(file)
}

func ReadBigText(file *os.File) {
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		println(scanner.Text())
	}
	// 輸出錯(cuò)誤
	println(scanner.Err().Error())
}

func CreateBigText() {
	file, err := os.Create("./read/test.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	data := make([]byte, 0, 32*1024)
	buffer := bytes.NewBuffer(data)
	// 構(gòu)造一個(gè)大的單行數(shù)據(jù)
	for i := 0; i < 50000; i++ {
		buffer.WriteString(strconv.Itoa(i))
	}
	// 寫(xiě)入一個(gè)換行符
	buffer.WriteByte('\n')
	buffer.WriteString("I love you yesterday and today!\n")
	buffer.WriteString("有一美人兮,見(jiàn)之不忘。\n")
	// 將3行寫(xiě)入文件
	file.Write(buffer.Bytes())
	log.Println("創(chuàng)建文件成功")
}

2.問(wèn)題探究

讓我們來(lái)探究一下這個(gè)問(wèn)題的原因,首先看一下Scan()方法的注釋?zhuān)@個(gè)方法就是每次掃描到下一個(gè)token,然后就可以通過(guò)獲取字節(jié)或者文本的方法來(lái)獲取掃描過(guò)的token。如果它返回值是false,就會(huì)返回掃描期間遇到的錯(cuò)誤,除了io.EOF.

Scan advances the Scanner to the next token, which will then be available through the Bytes or Text method. It returns false when the scan stops, either by reaching the end of the input or an error. After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil. Scan panics if the split function returns too many empty tokens without advancing the input. This is a common error mode for scanners.

所以Scan()和Text()函數(shù)是這樣結(jié)合起來(lái)使用的,首先Scan()會(huì)掃描出一個(gè)token,然后Text()將其轉(zhuǎn)成文本(或者其它方法轉(zhuǎn)成字節(jié)),循環(huán)執(zhí)行這種操作就可以按行讀取一個(gè)文件。

通過(guò)閱讀Scan()函數(shù)的源碼,我們可以發(fā)現(xiàn)這樣一個(gè)判斷,如果buf的長(zhǎng)度大于了最大token長(zhǎng)度,那就會(huì)報(bào)錯(cuò),見(jiàn)下圖。

繼續(xù)查找,可以看到最大長(zhǎng)度已經(jīng)定義好了,它的長(zhǎng)度是 64*1024 byte,即64KB,所以一行文本超過(guò)了這個(gè)最大長(zhǎng)度,那么就會(huì)報(bào)錯(cuò)!

3.問(wèn)題解決

其實(shí)大部分情況下我們都應(yīng)該使用Scan()函數(shù)結(jié)合Text()或者Bytes()函數(shù)來(lái)讀取文件的,這個(gè)也是官方推薦的,因?yàn)樗鼈兪?high-level 方法,用起來(lái)很方便。但是如果我們有一些極端的情況,例如單行超過(guò)64KB,那么怎么辦呢?(這種情況是很少的,但是又有可能會(huì)遇到這種需求的,例如文件里面存儲(chǔ)了一串Base64編碼)

這里可以這樣來(lái)使用,這個(gè)方法不會(huì)受到64KB的限制,ReaderString方法會(huì)按照指定的定界符來(lái)讀取一個(gè)完整的行,返回值是字符串和讀取遇到的錯(cuò)誤。如果想要讀取返回值為字節(jié)的話(huà),可以使用 ReadBytes 方法。

func ReadBigText(file *os.File) {
	defer file.Close()
	reader := bufio.NewReader(file)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%d %s", len(line), line)
	}
}

通過(guò)閱讀源碼可知,其實(shí)這個(gè)方法也是會(huì)遇到行太長(zhǎng)的問(wèn)題,只不過(guò)它忽略了這種情況。

ErrBufferFull就是這個(gè)緩沖區(qū)溢出錯(cuò)誤。

我們繼續(xù)進(jìn)入內(nèi)容其實(shí)也可以知道,它默認(rèn)的緩沖區(qū)大小是4KB。

4.擴(kuò)展

上面都說(shuō)相對(duì)高層的方法,我們來(lái)看一下相對(duì)底層的方法。

ReadLine is a low-level line-reading primitive. Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner.

ReadLine是讀取一行,但是它是一個(gè) low-level 方法,它會(huì)返回三個(gè)值:[]byte、isPrefix bool和err error。
其中最令人好奇的是第二個(gè)參數(shù),它如果是true,則表示當(dāng)前行沒(méi)有讀取完畢,但是緩沖區(qū)滿(mǎn)了,可以看下面這段注釋。

If the line was too long for the buffer then isPrefix is set and the beginning of the line is returned. The rest of the line will be returned from future calls.

func ReadBigText(file *os.File) {
	defer file.Close()
	reader := bufio.NewReader(file)

	for {
		bline, isPrefix, err := reader.ReadLine()
		if err == io.EOF {
			break // 讀取到文件結(jié)束才退出
		}
		// 讀取到超長(zhǎng)行,即單行超過(guò)4k字節(jié),直接寫(xiě)入文件,不對(duì)此行做處理
		if isPrefix {
			fmt.Print(string(bline))
			continue
		}

		fmt.Println(string(bline))
	}
}

不過(guò)需要注意這個(gè)方法讀取出來(lái)的數(shù)據(jù)是不包括換行符的,所以我是用的println打印輸出的。

如果你也去看了 ReadString、ReadBytesReadLine 方法,會(huì)發(fā)現(xiàn)兩種都依賴(lài)于一個(gè)底層的方法——ReadSlice方法。這個(gè)方法很原始,一般不會(huì)直接使用它。如果它遇到了超長(zhǎng)行,它就會(huì)直接返回讀取到的字節(jié)和一個(gè)ErrBufferFull,那這樣我們就可以根據(jù)這個(gè)錯(cuò)誤來(lái)繼續(xù)讀取數(shù)據(jù)了。這種方式還是相對(duì)麻煩了一些,不過(guò)如果你可以理解的話(huà),對(duì)于上面的方法也就不是問(wèn)題了。學(xué)習(xí)嘛,還是有必要一探究竟的。不過(guò)閱讀源碼感覺(jué)有些還是理解起來(lái)很困難,特別是這些英語(yǔ)注釋?zhuān)贿^(guò)也能看一個(gè)七七八八了。還不行的話(huà),那就再借助一些翻譯軟件,不過(guò)我個(gè)人覺(jué)得提高自己的英語(yǔ)能力還是非常必要的。

func ReadBigText(file *os.File) {
	defer file.Close()
	reader := bufio.NewReader(file)
	for {
		byt, err := reader.ReadSlice('\n')
		if err != nil {
			if err == bufio.ErrBufferFull {
				fmt.Print(string(byt))
				continue
			}
			log.Fatal(err)
		}
		fmt.Print(string(byt))
	}
}

總結(jié)

到此這篇關(guān)于Golang如何讀取單行超長(zhǎng)的文本的文章就介紹到這了,更多相關(guān)Golang讀取超長(zhǎng)文本內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang執(zhí)行cmd命令行的方法

    Golang執(zhí)行cmd命令行的方法

    本文主要介紹了Golang執(zhí)行cmd命令行的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • golang 實(shí)現(xiàn)并發(fā)求和

    golang 實(shí)現(xiàn)并發(fā)求和

    這篇文章主要介紹了golang 并發(fā)求和的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-05-05
  • Golang配置管理庫(kù)?Viper的教程詳解

    Golang配置管理庫(kù)?Viper的教程詳解

    這篇文章主要介紹了Golang?配置管理庫(kù)?Viper,使用?viper?能夠很好的去管理你的配置文件信息,比如數(shù)據(jù)庫(kù)的賬號(hào)密碼,服務(wù)器監(jiān)聽(tīng)的端口,你可以通過(guò)更改配置文件去更改這些內(nèi)容,而不用定位到那一段代碼上去,提高了開(kāi)發(fā)效率,需要的朋友可以參考下
    2022-05-05
  • GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧

    GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧

    這篇文章主要為大家介紹了GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 淺析Go語(yǔ)言中的Range關(guān)鍵字

    淺析Go語(yǔ)言中的Range關(guān)鍵字

    Range是go語(yǔ)言中很獨(dú)特的一個(gè)關(guān)鍵詞,也相當(dāng)好用。下面就跟著小編來(lái)再聊聊這個(gè)Range關(guān)鍵字,有需要的朋友們可以參考借鑒。
    2016-09-09
  • Go語(yǔ)言Zap庫(kù)Logger的定制化和封裝使用詳解

    Go語(yǔ)言Zap庫(kù)Logger的定制化和封裝使用詳解

    這篇文章主要介紹了Go語(yǔ)言Zap庫(kù)Logger的定制化和封裝使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • golang 并發(fā)編程之生產(chǎn)者消費(fèi)者詳解

    golang 并發(fā)編程之生產(chǎn)者消費(fèi)者詳解

    這篇文章主要介紹了golang 并發(fā)編程之生產(chǎn)者消費(fèi)者詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-05-05
  • golang調(diào)用c實(shí)現(xiàn)的dll接口細(xì)節(jié)分享

    golang調(diào)用c實(shí)現(xiàn)的dll接口細(xì)節(jié)分享

    這篇文章主要介紹了golang調(diào)用c實(shí)現(xiàn)的dll接口細(xì)節(jié)分享,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-05-05
  • golang雙鏈表的實(shí)現(xiàn)代碼示例

    golang雙鏈表的實(shí)現(xiàn)代碼示例

    這篇文章主要介紹了golang雙鏈表的實(shí)現(xiàn)代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • go語(yǔ)言中時(shí)間戳格式化的方法

    go語(yǔ)言中時(shí)間戳格式化的方法

    這篇文章主要介紹了go語(yǔ)言中時(shí)間戳格式化的方法,涉及Go語(yǔ)言中time的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03

最新評(píng)論