目录
问题内容
解决方法
首页 后端开发 Golang 在 Go 中忽略长文本文件中包含模式的行

在 Go 中忽略长文本文件中包含模式的行

Feb 13, 2024 pm 01:57 PM
go语言

在 Go 中忽略长文本文件中包含模式的行

php小编苹果在Go语言中,我们经常需要处理大型文本文件。有时,我们只对包含特定模式的行感兴趣,而忽略其他行。幸运的是,在Go中,我们可以使用正则表达式和bufio.Scanner来实现这个目标。通过使用正则表达式来匹配行,并通过Scanner来逐行处理文件,我们可以轻松地过滤掉我们不感兴趣的行。这个技巧不仅可以提高效率,还可以使我们的代码更加简洁和可读。接下来,让我们一起来看看如何在Go中实现忽略长文本文件中包含模式的行。

问题内容

我正在尝试实现一个函数来忽略 go 中长文本文件(保证 ascii)中包含模式的行

我在 withoutignorewithignore 下面的函数都接受文件名参数输入并返回 *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 的性能显着提高。

程序

  • 读取一个块,其中块大小应明显长于最大行长度,值 >= 64kb 可能应该不错,在测试中使用了问题中的 1mb。
  • 一个块通常不会以换行符结束,因此从块的末尾搜索到下一个换行符,将搜索限制在这个切片并记住下一次传递的剩余数据
  • 最后一个块不一定以换行符结尾
  • 在高性能 go 函数 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中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Go语言中用于浮点数运算的库有哪些? Go语言中用于浮点数运算的库有哪些? Apr 02, 2025 pm 02:06 PM

Go语言中用于浮点数运算的库介绍在Go语言(也称为Golang)中,进行浮点数的加减乘除运算时,如何确保精度是�...

Go的爬虫Colly中Queue线程的问题是什么? Go的爬虫Colly中Queue线程的问题是什么? Apr 02, 2025 pm 02:09 PM

Go爬虫Colly中的Queue线程问题探讨在使用Go语言的Colly爬虫库时,开发者常常会遇到关于线程和请求队列的问题。�...

在 Go 语言中,为什么使用 Println 和 string() 函数打印字符串会出现不同的效果? 在 Go 语言中,为什么使用 Println 和 string() 函数打印字符串会出现不同的效果? Apr 02, 2025 pm 02:03 PM

Go语言中字符串打印的区别:使用Println与string()函数的效果差异在Go...

在Go语言中使用Redis Stream实现消息队列时,如何解决user_id类型转换问题? 在Go语言中使用Redis Stream实现消息队列时,如何解决user_id类型转换问题? Apr 02, 2025 pm 04:54 PM

Go语言中使用RedisStream实现消息队列时类型转换问题在使用Go语言与Redis...

GoLand中自定义结构体标签不显示怎么办? GoLand中自定义结构体标签不显示怎么办? Apr 02, 2025 pm 05:09 PM

GoLand中自定义结构体标签不显示怎么办?在使用GoLand进行Go语言开发时,很多开发者会遇到自定义结构体标签在�...

Go语言中哪些库是由大公司开发或知名的开源项目提供的? Go语言中哪些库是由大公司开发或知名的开源项目提供的? Apr 02, 2025 pm 04:12 PM

Go语言中哪些库是大公司开发或知名开源项目?在使用Go语言进行编程时,开发者常常会遇到一些常见的需求,�...

Go语言中`var`和`type`关键字定义结构体的区别是什么? Go语言中`var`和`type`关键字定义结构体的区别是什么? Apr 02, 2025 pm 12:57 PM

Go语言中结构体定义的两种方式:var与type关键字的差异Go语言在定义结构体时,经常会看到两种不同的写法:一�...

使用 sql.Open 时,DSN 传空为什么不报错? 使用 sql.Open 时,DSN 传空为什么不报错? Apr 02, 2025 pm 12:54 PM

使用sql.Open时,DSN传空为什么不报错?在Go语言中,sql.Open...

See all articles