Heim > Artikel > Backend-Entwicklung > Ignorieren Sie Zeilen mit Mustern in der Langtextdatei in Go
php-Editor Apple In der Go-Sprache müssen wir oft große Textdateien verarbeiten. Manchmal sind wir nur an Zeilen interessiert, die ein bestimmtes Muster enthalten, und ignorieren andere Zeilen. Glücklicherweise können wir in Go reguläre Ausdrücke und bufio.Scanner verwenden, um dieses Ziel zu erreichen. Indem wir reguläre Ausdrücke zum Abgleichen von Zeilen verwenden und die Datei Zeile für Zeile durch einen Scanner laufen lassen, können wir Zeilen, die uns nicht interessieren, ganz einfach herausfiltern. Dieser Tipp verbessert nicht nur die Effizienz, sondern macht unseren Code auch prägnanter und lesbarer. Schauen wir uns als Nächstes an, wie man Zeilen mit Mustern in langen Textdateien in Go ignoriert.
Ich versuche, eine Funktion zu implementieren, um Zeilen mit Mustern in langen Textdateien (garantiert ASCII) in go zu ignorieren
Ich bin withoutignore
和 withignore
下面的函数都接受文件名参数输入并返回 *byte.buffer
,随后可用于写入 io.writer
.
withignore
函数采用附加参数 pattern
从文件中排除包含模式的行。该函数可以工作,但通过基准测试,发现它比 慢 5 倍而不忽略
. Gibt es eine Möglichkeit, es zu verbessern?
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) } }
Benchmarks
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) } } }
Und kann über die Befehlszeile verwendet werden“base64dump.log”生成
base64 /dev/urandom | head -c 10000000 > base64dump.log
Da ASCII garantiert ist, kann es direkt auf der byte
-Ebene funktionieren.
Wenn Sie jedoch beim Lesen der Eingabe jedes Byte auf ein Zeilenumbruchzeichen überprüfen und dann innerhalb der Zeile erneut nach dem Muster suchen, wird die Operation auf jedes Byte angewendet.
Wenn Sie andererseits einen Eingabeblock lesen und eine optimierte Suche nach Mustern im Text durchführen, ohne jedes Eingabebyte zu überprüfen, können Sie die Operationen pro Eingabebyte minimieren.
Zum Beispiel der Boyer-Moore-String-Suchalgorithmus. Auch die integrierte bytes.index
-Funktion von Go wurde optimiert. Die erreichte Geschwindigkeit hängt natürlich von den Eingabedaten und dem tatsächlichen Modus ab. Für die in der Frage angegebene Eingabe verbessert sich die Leistung von „bytes.index“ bei Messung erheblich.
Programm
bytes.index
können Sie herausfinden, wo im Block das Muster auftritt Bemerkenswert
Ein Lesevorgang kann weniger Daten als die Blockgröße zurückgeben, daher ist es sinnvoll, den Lesevorgang zu wiederholen, bis die Blockgröße der Daten gelesen ist.
Benchmark
Optimierter Code ist in der Regel deutlich komplexer, performt aber auch deutlich besser, wie wir später sehen werden.
benchmarktestwithoutignore-8 270 4137267 ns/op benchmarktestwithignore-8 54 22403931 ns/op benchmarktestfilter-8 150 7947454 ns/op
Hier ist die optimierte Code-benchmarktestfilter-8
只比没有过滤的操作慢1.9倍左右,而benchmarktestwithignore-8
Methode 5,4-mal langsamer als der ungefilterte Vergleichswert.
Betrachten Sie es aus einer anderen Perspektive: Der optimierte Code ist 2,8-mal schneller als der nicht optimierte Code.
Code
Natürlich ist hier der Code für Ihre eigenen Tests:
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 }
Der Benchmark-Bereich könnte so aussehen:
func BenchmarkTestFilter(b *testing.B) { for i := 0; i < b.N; i++ { _, err := filterFile("base64dump.log", "AUDIT") if err != nil { b.Fatal(err) } } }
Die Filterfunktion ist aufgeteilt und die eigentliche Arbeit wird in func filter(reader io.reader,pattern []byte, chunksize int) (*bytes.buffer, error)
erledigt.
Die Erstellung von Unit-Tests wurde durch das Einfügen von Readern und Chunksize vorbereitet oder in Betracht gezogen. Dies fehlt hier, ist aber beim Umgang mit Indizes unbedingt zu empfehlen.
Allerdings geht es hier darum, einen Weg zu finden, die Leistung deutlich zu verbessern.
Das obige ist der detaillierte Inhalt vonIgnorieren Sie Zeilen mit Mustern in der Langtextdatei in Go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!