Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Abaikan baris yang mengandungi corak dalam fail teks panjang dalam Go

Abaikan baris yang mengandungi corak dalam fail teks panjang dalam Go

WBOY
WBOYke hadapan
2024-02-13 13:57:19934semak imbas

在 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.

Kandungan soalan

Saya cuba melaksanakan fungsi untuk mengabaikan baris yang mengandungi corak dalam fail teks panjang (aci terjamin) dalam perjalanan

Saya withoutignorewithignore 下面的函数都接受文件名参数输入并返回 *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

Penyelesaian

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

  • Membaca blok di mana saiz blok harus jauh lebih panjang daripada panjang garis maksimum, nilai >= 64kb mungkin bagus, dalam ujian saya menggunakan 1mb dalam soalan.
  • Sebuah blok biasanya tidak berakhir dengan baris baharu, jadi cari dari hujung blok ke baris baharu seterusnya, hadkan carian kepada kepingan ini dan ingat baki data untuk laluan seterusnya
  • Blok terakhir tidak semestinya berakhir dengan watak baris baharu
  • Dengan bantuan fungsi go berprestasi tinggi bytes.index anda boleh mencari di mana dalam blok corak itu berlaku
  • Cari aksara baris baharu sebelum dan seterusnya dari kedudukan ditemui
  • Blok kemudian dikeluarkan ke permulaan baris yang sepadan
  • Dan teruskan mencari dari hujung baris di mana corak muncul
  • Jika carian tidak menemui lokasi lain, keluarkan lokasi yang tinggal
  • Baca bahagian seterusnya dan gunakan langkah yang diterangkan sekali lagi sehingga anda sampai ke penghujung fail

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!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam