首页 > 解决方案 > Golang替换任何和所有换行符

问题描述

通常,当我替换换行符时,我会跳转到正则表达式,就像在这个 PHP 中一样

preg_replace('/\R/u', "\n", $String);

因为我知道这是替换任何类型的 Unicode 换行符(无论是 \n、\r、\r\n 等)的一种非常持久的方法。

我也在 Go 中尝试过这样的事情,但我明白了

解析正则表达式时出错:无效的转义序列:\R

在这条线上

msg = regexp.MustCompilePOSIX("\\R").ReplaceAllString(html.EscapeString(msg), "<br>\n")

我尝试(?:(?>\r\n)|\v)https://stackoverflow.com/a/4389171/728236使用,但看起来 Go 的正则表达式实现也不支持,恐慌invalid or unsupported Perl syntax: '(?>'

在 Go、Regex 中替换换行符的好、安全的方法是什么?


我在这里看到了这个答案Golang: Issues replace newlines in a string from a text file say to use \r?\n,但我犹豫是否相信它会得到所有Unicode 换行符,主要是因为这个问题的答案列出了更多的换行符代码点\r?\n涵盖的 3 个,

标签: regexstringgo

解决方案


虽然使用正则表达式通常会产生一个优雅而紧凑的解决方案,但它通常不是最快的。

对于必须用其他子字符串替换某些子字符串的任务,标准库以以下形式提供了一个非常有效的解决方案strings.Replacer

Replacer 用替换替换字符串列表。多个 goroutine 并发使用它是安全的。

您可以使用 来创建可重复使用的替换器strings.NewReplacer(),在其中列出包含可替换部件及其替换的对。当您想要执行替换时,您只需调用Replacer.Replace().

这是它的样子:

const replacement = "<br>\n"

var replacer = strings.NewReplacer(
    "\r\n", replacement,
    "\r", replacement,
    "\n", replacement,
    "\v", replacement,
    "\f", replacement,
    "\u0085", replacement,
    "\u2028", replacement,
    "\u2029", replacement,
)

func replaceReplacer(s string) string {
    return replacer.Replace(s)
}

以下是Wiktor 回答中的正则表达式解决方案的样子:

var re = regexp.MustCompile(`\r\n|[\r\n\v\f\x{0085}\x{2028}\x{2029}]`)

func replaceRegexp(s string) string {
    return re.ReplaceAllString(s, "<br>\n")
}

实施实际上相当快。这是一个简单的基准,将其与上述预编译的正则表达式解决方案进行比较:

const input = "1st\nsecond\r\nthird\r4th\u0085fifth\u2028sixth"

func BenchmarkReplacer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        replaceReplacer(input)
    }
}

func BenchmarkRegexp(b *testing.B) {
    for i := 0; i < b.N; i++ {
        replaceRegexp(input)
    }
}

以及基准测试结果:

BenchmarkReplacer-4      3000000               495 ns/op
BenchmarkRegexp-4         500000              2787 ns/op

对于我们的测试输入,速度快strings.Replacer5 倍以上

还有另一个优势。在上面的示例中,我们将结果作为新string值获得(在两种解决方案中)。这需要新的string分配。如果我们需要将结果写入 an io.Writer(例如,我们正在创建 HTTP 响应或将结果写入文件),我们可以避免创建 newstring以防万一,strings.Replacer因为它有一个方便的Replacer.WriteString()方法,它接受io.Writer并写入结果进入它而不分配和返回它作为一个string. 与正则表达式解决方案相比,这进一步显着提高了性能增益。


推荐阅读