Rumah >pembangunan bahagian belakang >Golang >Abaikan baris yang mengandungi corak dalam fail teks panjang dalam Go
php Editor Apple Dalam bahasa Go, kita selalunya perlu memproses fail teks yang besar. Kadangkala kami hanya berminat dengan baris yang mengandungi corak tertentu dan mengabaikan baris lain. Nasib baik, dalam Go, kita boleh menggunakan ungkapan biasa dan bufio.Scanner untuk mencapai matlamat ini. Dengan menggunakan ungkapan biasa untuk memadankan baris dan menjalankan fail melalui Pengimbas baris demi baris, kami boleh menapis dengan mudah baris yang kami tidak minati. Petua ini bukan sahaja meningkatkan kecekapan, tetapi juga menjadikan kod kami lebih ringkas dan boleh dibaca. Seterusnya, mari kita lihat cara mengabaikan baris yang mengandungi corak dalam fail teks panjang dalam Go.
Saya cuba melaksanakan fungsi untuk mengabaikan baris yang mengandungi corak dalam fail teks panjang (aci terjamin) dalam perjalanan
Saya withoutignore
和 withignore
下面的函数都接受文件名参数输入并返回 *byte.buffer
,随后可用于写入 io.writer
.
withignore
函数采用附加参数 pattern
从文件中排除包含模式的行。该函数可以工作,但通过基准测试,发现它比 慢 5 倍而不忽略
. Adakah terdapat apa-apa cara ia boleh diperbaiki?
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) } }
Tanda aras
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) } } }
Dan boleh digunakan dari baris arahan“base64dump.log”生成
base64 /dev/urandom | head -c 10000000 > base64dump.log
Oleh kerana ascii dijamin, ia boleh berfungsi secara langsung pada tahap byte
.
Walau bagaimanapun, jika anda menyemak setiap bait untuk aksara baris baharu semasa membaca input, dan kemudian mencari dalam baris itu sekali lagi untuk corak, operasi akan digunakan pada setiap bait.
Sebaliknya, jika anda membaca blok input dan melakukan carian yang dioptimumkan untuk corak dalam teks, tanpa menyemak setiap bait input, anda boleh meminimumkan operasi setiap bait input.
Sebagai contoh, algoritma carian rentetan boyer-moore. Fungsi bytes.index
terbina dalam Go juga telah dioptimumkan. Kelajuan yang dicapai sudah tentu bergantung pada data input dan mod sebenar. Untuk input yang dinyatakan dalam soalan, prestasi `bytes.index meningkat dengan ketara apabila diukur.
Program
bytes.index
anda boleh mencari di mana dalam blok corak itu berlaku Perhatian
Operasi baca mungkin mengembalikan data yang kurang daripada saiz blok, jadi masuk akal untuk mengulang operasi baca sehingga saiz blok data dibaca.
Tanda aras
Kod yang dioptimumkan biasanya jauh lebih kompleks, tetapi juga menunjukkan prestasi yang lebih baik, seperti yang akan kita lihat nanti.
benchmarktestwithoutignore-8 270 4137267 ns/op benchmarktestwithignore-8 54 22403931 ns/op benchmarktestfilter-8 150 7947454 ns/op
Di sini, kaedah benchmarktestfilter-8
只比没有过滤的操作慢1.9倍左右,而benchmarktestwithignore-8
kod yang dioptimumkan adalah 5.4 kali lebih perlahan daripada nilai perbandingan yang tidak ditapis.
Lihat dari perspektif lain: kod yang dioptimumkan adalah 2.8 kali lebih pantas daripada kod yang tidak dioptimumkan.
kod
Sudah tentu, berikut ialah kod untuk ujian anda sendiri:
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 }
Bahagian penanda aras mungkin kelihatan seperti ini:
func BenchmarkTestFilter(b *testing.B) { for i := 0; i < b.N; i++ { _, err := filterFile("base64dump.log", "AUDIT") if err != nil { b.Fatal(err) } } }
Fungsi penapis berpecah dan kerja sebenar dilakukan dalam func filter(reader io.reader,pattern []byte, chunksize int) (*bytes.buffer, error)
.
Penciptaan ujian unit telah disediakan atau diambil kira dengan menyuntik pembaca dan chunksize, yang tiada di sini tetapi pasti disyorkan apabila berurusan dengan indeks.
Walau bagaimanapun, perkara di sini adalah untuk mencari cara untuk meningkatkan prestasi dengan ketara.
Atas ialah kandungan terperinci Abaikan baris yang mengandungi corak dalam fail teks panjang dalam Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!