php小編蘋果在Go語言中,我們經常需要處理大型文字檔。有時,我們只對包含特定模式的行感興趣,而忽略其他行。幸運的是,在Go中,我們可以使用正規表示式和bufio.Scanner來實現這個目標。透過使用正規表示式來匹配行,並透過Scanner來逐行處理文件,我們可以輕鬆地過濾掉我們不感興趣的行。這個技巧不僅可以提高效率,還可以讓我們的程式碼更加簡潔和可讀。接下來,讓我們一起來看看如何在Go中實作忽略長文字檔案中包含模式的行。
我正在嘗試實作一個函數來忽略 go 中長文字檔案(保證 ascii)中包含模式的行
我在withoutignore
和withignore
下面的函數都接受檔名參數輸入並傳回*byte.buffer
,隨後可用於寫入 io.writer
。
withignore
函數採用附加參數 pattern
從檔案中排除包含模式的行。該函數可以工作,但通過基準測試,發現它比 慢 5 倍而不忽略
。有什麼辦法可以改進嗎?
package main import ( "bufio" "bytes" "io" "log" "os" ) func withoutignore(f string) (*bytes.buffer, error) { rfd, err := os.open(f) if err != nil { log.fatal(err) } defer func() { if err := rfd.close(); err != nil { log.fatal(err) } }() inputbuffer := make([]byte, 1048576) var bytesread int var bs []byte opbuffer := bytes.newbuffer(bs) for { bytesread, err = rfd.read(inputbuffer) if err == io.eof { return opbuffer, nil } if err != nil { return nil, nil } _, err = opbuffer.write(inputbuffer[:bytesread]) if err != nil { return nil, err } } return opbuffer, nil } func withignore(f, pattern string) (*bytes.buffer, error) { rfd, err := os.open(f) if err != nil { log.fatal(err) } defer func() { if err := rfd.close(); err != nil { log.fatal(err) } }() scanner := bufio.newscanner(rfd) var bs []byte buffer := bytes.newbuffer(bs) for scanner.scan() { if !bytes.contains(scanner.bytes(), []byte(pattern)) { _, err := buffer.writestring(scanner.text() + "\n") if err != nil { return nil, nil } } } return buffer, nil } func main() { // buff, err := withoutignore("base64dump.log") buff, err := withignore("base64dump.log", "audit") if err != nil { log.fatal(err) } _, err = buff.writeto(os.stdout) if err != nil { log.fatal(err) } }
基準測試
package main import "testing" func benchmarktestwithoutignore(b *testing.b) { for i := 0; i < b.n; i++ { _, err := withoutignore("base64dump.log") if err != nil { b.fatal(err) } } } func benchmarktestwithignore(b *testing.b) { for i := 0; i < b.n; i++ { _, err := withignore("base64dump.log", "audit") if err != nil { b.fatal(err) } } }
並且可以在命令列中使用“base64dump.log”產生
base64 /dev/urandom | head -c 10000000 > base64dump.log
由於ascii是有保證的,所以可以直接在byte
層級工作。
不過,如果在讀取輸入時檢查每個位元組是否有換行符,然後再次在行內搜尋模式,則操作將應用於每個位元組。
另一方面,如果讀取輸入區塊並對文字中的模式執行最佳化搜索,甚至不檢查每個輸入字節,則可以最大限度地減少每個輸入位元組的操作。
例如,boyer-moore 字串搜尋演算法。 go內建的bytes.index
函數也進行了最佳化。所達到的速度當然取決於輸入資料和實際模式。對於問題中指定的輸入,測量時`bytes.index 的性能顯著提高。
程式
bytes.index
的幫助下,您可以找到該模式在區塊中出現的位置值得注意
讀取操作傳回的資料可能少於區塊大小,因此重複讀取操作直到讀取區塊大小的資料是有意義的。
基準
優化後的程式碼通常要複雜得多,但效能也明顯更好,我們稍後會看到。
benchmarktestwithoutignore-8 270 4137267 ns/op benchmarktestwithignore-8 54 22403931 ns/op benchmarktestfilter-8 150 7947454 ns/op
這裡,優化後的程式碼benchmarktestfilter-8
只比沒有過濾的操作慢1.9倍左右,而benchmarktestwithignore-8
方法比沒有過濾的比較值慢5.4倍。
從另一個角度來看:優化後的程式碼比未優化的程式碼快2.8 倍。
程式碼
當然,這是您自己測試的程式碼:
func filterfile(f, pattern string) (*bytes.buffer, error) { rfd, err := os.open(f) if err != nil { log.fatal(err) } defer func() { if err := rfd.close(); err != nil { log.fatal(err) } }() reader := bufio.newreader(rfd) return filter(reader, []byte(pattern), 1024*1024) } // chunksize must be larger than the longest line // a reasonable size is probably >= 64k func filter(reader io.reader, pattern []byte, chunksize int) (*bytes.buffer, error) { var bs []byte buffer := bytes.newbuffer(bs) chunk := make([]byte, chunksize) var remaining []byte for lastchunk := false; !lastchunk; { n, err := readchunk(reader, chunk, remaining, chunksize) if err != nil { if err == io.eof { lastchunk = true } else { return nil, err } } remaining = remaining[:0] if !lastchunk { for i := n - 1; i > 0; i-- { if chunk[i] == '\n' { remaining = append(remaining, chunk[i+1:n]...) n = i + 1 break } } } s := 0 for s < n { hit := bytes.index(chunk[s:n], pattern) if hit < 0 { break } hit += s startofline := hit for ; startofline > 0; startofline-- { if chunk[startofline] == '\n' { startofline++ break } } endofline := hit + len(pattern) for ; endofline < n; endofline++ { if chunk[endofline] == '\n' { break } } endofline++ _, err = buffer.write(chunk[s:startofline]) if err != nil { return nil, err } s = endofline } if s < n { _, err = buffer.write(chunk[s:n]) if err != nil { return nil, err } } } return buffer, nil } func readchunk(reader io.reader, chunk, remaining []byte, chunksize int) (int, error) { copy(chunk, remaining) r := len(remaining) for r < chunksize { n, err := reader.read(chunk[r:]) r += n if err != nil { return r, err } } return r, nil }
基準部分可能看起來像這樣:
func BenchmarkTestFilter(b *testing.B) { for i := 0; i < b.N; i++ { _, err := filterFile("base64dump.log", "AUDIT") if err != nil { b.Fatal(err) } } }
濾波器函數被拆分,實際工作在 func filter(reader io.reader,pattern []byte, chunksize int) (*bytes.buffer, error)
中完成。
透過注入讀取器和 chunksize,已經準備或考慮了單元測試的創建,這裡缺少這一點,但在處理索引時絕對建議這樣做。
但是,這裡的要點是找到一種方法來顯著提高效能。
以上是在 Go 中忽略長文字檔案中包含模式的行的詳細內容。更多資訊請關注PHP中文網其他相關文章!