E1.1 Go語(yǔ)言實(shí)現(xiàn)超大文本文件按行排序和去重復(fù) -- 準(zhǔn)備工作:文件行數(shù)與平均字符數(shù)預(yù)覽

給文本文件按行為單位進(jìn)行排序和去除重復(fù)行,是文本處理任務(wù)中非常常見(jiàn)的,尤其是對(duì)超大文本文件的排序和去重,由于所需空間可能超出計(jì)算機(jī)內(nèi)存的大小,無(wú)法一次性加載進(jìn)內(nèi)存進(jìn)行簡(jiǎn)單的處理,因此是一個(gè)有挑戰(zhàn)性的難題:既要保證能夠處理,又要保證處理速度快。本文就是為此做準(zhǔn)備,首先實(shí)現(xiàn)預(yù)覽超大文本文件,統(tǒng)計(jì)總行數(shù)和平均每行的字符數(shù),并且為了便于增加直觀的了解,增加了預(yù)覽前100行的功能。

本例中,使用了第三方包github.com/topxeq/tk,該包中有不少實(shí)用的函數(shù),我們?cè)谝院笠矊?huì)經(jīng)常用到??梢杂孟率雒畎惭b該包。

go get -v github.com/topxeq/tk

本文例子代碼中已經(jīng)加了很多代碼內(nèi)注釋,注意Go語(yǔ)言從C語(yǔ)言發(fā)展而來(lái),一定程度上可以看成C語(yǔ)言的超級(jí)加強(qiáng)版,因此在注釋的語(yǔ)法上和C/C++語(yǔ)言是一致的,都支持“//”開(kāi)始的單行注釋,和“/*...*/”包起來(lái)的多行注釋。

本文中的代碼可以在github.com/topxeq/goexamples/preview1中找到,可以用

go get -v?github.com/topxeq/goexamples/preview1

命令獲取,也可以直接用

go get -v?github.com/topxeq/goexamples

命令獲取所有例子代碼后。獲取后,到GOPATH目錄下找到該子目錄(如果GOPATH是C:\goprjs,則最終代碼包目錄為C:\goprjs\src\github.com/topxeq/goexamples/preview1),在該目錄下運(yùn)行下述命令即可執(zhí)行,

go run preview1.go test1.txt

其中test1.txt是準(zhǔn)備預(yù)覽的超大文本文件。

代碼如下:

package?main

import?(

????"bufio"

????"io"

????"os"

????"strings"

????//?本例中使用了第三方包tk,可用go?get?-v?github.com/topxeq/tk命令安裝

????"github.com/topxeq/tk"

)

func?main()?{

????//?獲取第1個(gè)命令行參數(shù)(實(shí)際上是第二個(gè)命令行參數(shù),可執(zhí)行文件名是第1個(gè),序號(hào)為0)

????fileNameT?:=?tk.GetParameterByIndexWithDefaultValue(os.Args,?1,?"")

????//?如果命令行參數(shù)中沒(méi)有指定文件名則報(bào)錯(cuò)

????if?fileNameT?==?""?{

????????tk.Pl("文件名不能為空")

????????//?等待用戶輸入(可以輸入任意字符,回車鍵結(jié)束)

????????tk.GetUserInput("按回車鍵退出……")

????????return

????}

????//?打開(kāi)文件

????fileT,?errT?:=?os.Open(fileNameT)

????if?errT?!=?nil?{

????????tk.Pl("打開(kāi)文件%v時(shí)發(fā)生錯(cuò)誤:%v。",?fileNameT,?errT)

????????tk.Pl("請(qǐng)按回車鍵結(jié)束處理……")

????????tk.GetUserInput("按回車鍵退出……")

????????return

????}

????//?保證關(guān)閉文件(在函數(shù)退出時(shí)會(huì)執(zhí)行該條語(yǔ)句)

????defer?fileT.Close()

????//?創(chuàng)建讀取文件的緩沖式讀取器

????readerT?:=?bufio.NewReader(fileT)

????//?記錄總行數(shù)

????countT?:=?0

????//?記錄總字符數(shù)

????totalLenT?:=?0

????//?循環(huán)讀取文件中的每一行

????for?true?{

????????strT,?errT?:=?readerT.ReadString('\n')

????????//?如果出現(xiàn)錯(cuò)誤則中止循環(huán)

????????if?errT?!=?nil?{

????????????//?錯(cuò)誤有可能是到達(dá)文件末尾,此時(shí)正常終止循環(huán)

????????????if?errT?==?io.EOF?{

????????????????strT?=?strings.TrimRight(strT,?"\r\n")

????????????????if?countT?<?100?{

????????????????????tk.Pl("%v:?%v",?countT+1,?strT)

????????????????}

????????????????totalLenT?+=?len(strT)

????????????????countT++

????????????}

????????????break

????????}

????????//?去除行尾可能存在的\r和\n字符(Windows中的文本文件一般的行尾結(jié)束符是連續(xù)的\r\n兩個(gè)字符)

????????strT?=?strings.TrimRight(strT,?"\r\n")

????????//?100行以內(nèi)會(huì)輸出預(yù)覽,并輸出行號(hào)

????????if?countT?<?100?{

????????????tk.Pl("%v:?%v",?countT+1,?strT)

????????}

????????//?增加總字符數(shù)和總行數(shù)

????????totalLenT?+=?len(strT)

????????countT++

????}

????tk.Pl("共%v行,平均每行%v個(gè)字符。",?countT,?totalLenT/countT)

????tk.GetUserInput("按回車鍵退出……")

????return

}



除了代碼的內(nèi)部文檔注釋,再補(bǔ)充說(shuō)明如下:

->?tk.Pl函數(shù)類似于fmt.Printf函數(shù),但多輸出一個(gè)回車換行;

->?tk.GetUserInput函數(shù)用于獲取用戶輸入,本例中實(shí)際上等待一個(gè)回車后好再退出程序;這是為了直接雙擊該程序可執(zhí)行文件運(yùn)行的情況下,如果沒(méi)有該條語(yǔ)句,CMD窗口會(huì)在程序退出時(shí)直接一閃消失,導(dǎo)致看不清程序的輸出,因此增加的便捷功能;

-> defer關(guān)鍵字是Go語(yǔ)言中的特性之一,其特點(diǎn)是:保證defer后面的語(yǔ)句(同一行內(nèi),一般為一個(gè)函數(shù))可以在任何情況下都可以被執(zhí)行,無(wú)論是函數(shù)正常退出還是異常中止。如果定義了多條defer語(yǔ)句,則會(huì)在正?;虍惓M顺鏊诘暮瘮?shù)時(shí)按定義時(shí)的反向順序執(zhí)行,即先定義的后執(zhí)行;

-> 本例中使用了bufio包中定義的Reader類,可以保證用緩沖的方式讀取,寫文件時(shí)也可以用緩沖的方式,保護(hù)一下硬盤;

-> 用bufio.Reader.ReadString函數(shù)讀取到的字符串,會(huì)包含最后的行結(jié)束符“\r\n”(Linux系統(tǒng)下只有\(zhòng)n),因此需要用strings.TrimRight函數(shù)去除行尾的這兩個(gè)字符,注意該函數(shù)第二個(gè)參數(shù)是指要截取的所有字符,即每個(gè)字符都會(huì)被從行尾去除掉,而不是按順序的兩個(gè)字符去除;


這段代碼執(zhí)行的示例截圖如下:


注意,為了演示效果清晰,本例中的代碼僅輸出了前十行預(yù)覽??梢钥闯?,該超大文本文件包含4千多萬(wàn)行,平均每行約20個(gè)字符,因此總大小在1G左右。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容