搜索
首页后端开发Golang在 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中文网其他相关文章!

声明
本文转载于:stackoverflow。如有侵权,请联系admin@php.cn删除
Golang:Go编程语言解释了Golang:Go编程语言解释了Apr 10, 2025 am 11:18 AM

Go语言的核心特性包括垃圾回收、静态链接和并发支持。1.Go语言的并发模型通过goroutine和channel实现高效并发编程。2.接口和多态性通过实现接口方法,使得不同类型可以统一处理。3.基本用法展示了函数定义和调用的高效性。4.高级用法中,切片提供了动态调整大小的强大功能。5.常见错误如竞态条件可以通过gotest-race检测并解决。6.性能优化通过sync.Pool重用对象,减少垃圾回收压力。

Golang的目的:建立高效且可扩展的系统Golang的目的:建立高效且可扩展的系统Apr 09, 2025 pm 05:17 PM

Go语言在构建高效且可扩展的系统中表现出色,其优势包括:1.高性能:编译成机器码,运行速度快;2.并发编程:通过goroutines和channels简化多任务处理;3.简洁性:语法简洁,降低学习和维护成本;4.跨平台:支持跨平台编译,方便部署。

SQL排序中ORDER BY语句结果为何有时看似随机?SQL排序中ORDER BY语句结果为何有时看似随机?Apr 02, 2025 pm 05:24 PM

关于SQL查询结果排序的疑惑学习SQL的过程中,常常会遇到一些令人困惑的问题。最近,笔者在阅读《MICK-SQL基础�...

技术栈收敛是否仅仅是技术栈选型的过程?技术栈收敛是否仅仅是技术栈选型的过程?Apr 02, 2025 pm 05:21 PM

技术栈收敛与技术选型的关系在软件开发中,技术栈的选择和管理是一个非常关键的问题。最近,有读者提出了...

如何在Go语言中使用反射对比并处理三个结构体的差异?如何在Go语言中使用反射对比并处理三个结构体的差异?Apr 02, 2025 pm 05:15 PM

Go语言中如何对比并处理三个结构体在Go语言编程中,有时需要对比两个结构体的差异,并将这些差异应用到第�...

在Go语言中如何查看全局安装的包?在Go语言中如何查看全局安装的包?Apr 02, 2025 pm 05:12 PM

在Go语言中如何查看全局安装的包?在使用Go语言开发过程中,经常会使用go...

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

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

See all articles

热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.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器