go - 从大文本中删除所有非字母数字字符的有效方法

标签 go

我需要处理大量文本,其中一个步骤是删除所有非字母数字字符。我正试图找到一种有效的方法来做到这一点。

到目前为止我有两个功能:

func stripMap(str, chr string) string {
    return strings.Map(func(r rune) rune {
        if strings.IndexRune(chr, r) < 0 {
            return r
        }
        return -1
    }, str)
}

在这里我实际上必须提供所有非字母字符的字符串。

和普通的旧正则表达式

func stripRegex(in string) string {
    reg, _ := regexp.Compile("[^a-zA-Z0-9 ]+")
    return reg.ReplaceAllString(in, "")
}

正则表达式似乎慢得多

BenchmarkStripMap-8        30000         37907 ns/op        8192 B/op          2 allocs/op

BenchmarkStripRegex-8          10000        131449 ns/op       57552 B/op         35 allocs/op

寻求建议。还有其他更好的方法吗?改善以上?

最佳答案

因为幸存的 rune 少于utf8.RuneSelf ,这个问题可以通过对字节进行操作来解决。如果任何字节不在 [^a-zA-Z0-9 ] 中,则该字节是要删除的 rune 的一部分。

func strip(s string) string {
    var result strings.Builder
    for i := 0; i < len(s); i++ {
        b := s[i]
        if ('a' <= b && b <= 'z') ||
            ('A' <= b && b <= 'Z') ||
            ('0' <= b && b <= '9') ||
            b == ' ' {
            result.WriteByte(b)
        }
    }
    return result.String()
}

此函数的一个变体是通过调用 result.Grow 来预分配结果:

func strip(s string) string {
    var result strings.Builder
    result.Grow(len(s))
    ...

这确保函数进行一次内存分配,但如果幸存 rune 与源 rune 的比率很低,则内存分配可能会比需要的大得多。

此答案中的 strip 函数被编写为与 string 参数和结果类型一起使用,因为这些是问题中使用的类型。

如果应用程序正在处理 []byte 源文本并且可以修改该源文本,那么就地更新 []byte 会更有效.为此,将幸存的字节复制到 slice 的开头并在完成后重新 slice 。这避免了 strings.Builder 中的内存分配和开销。这种变化类似于 peterSO 对这个问题的回答。

func strip(s []byte) []byte {
    n := 0
    for _, b := range s {
        if ('a' <= b && b <= 'z') ||
            ('A' <= b && b <= 'Z') ||
            ('0' <= b && b <= '9') ||
            b == ' ' {
            s[n] = b
            n++
        }
    }
    return s[:n]
}

根据使用的实际数据,此答案中的一种方法可能比问题中的方法更快。

关于go - 从大文本中删除所有非字母数字字符的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54461423/

相关文章:

go - 无法将 nil 转换为类型字符串

go - 在 Jenkins 中构建的路径名不正确

go - 为什么 go 方法接收者应该保持一致?

Golang channel 卡住

go - 我可以在共享结构中允许任意字段吗?

go - go 中的子类型父类(super class)型

go - 为什么与 == 相比,具有相同日期和时间的 2 个时间结构返回 false?

regex - 如何在Golang中验证以逗号分隔的字符串?

linux - 在 Go 中设置进程名称(如 `ps` 所示)

android - 如何在 Crashlytics 中获取 golang 的 Stack-trace