【问题标题】:Golang replace any and all newline charactersGolang替换任何和所有换行符
【发布时间】:2019-03-06 17:37:13
【问题描述】:

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

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

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

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

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

在这条线上

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

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

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


我在这里看到这个答案 Golang: Issues replacing newlines in a string from a text file 说要使用 \r?\n,但我不敢相信它会得到 all Unicode 换行符,主要是因为这个问题的答案列出了很多换行代码点比 \r?\n 覆盖的 3 个代码点多,

【问题讨论】:

标签: regex string go


【解决方案1】:

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

对于必须用其他子字符串替换某些子字符串的任务,标准库以strings.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's answer 的正则表达式解决方案如下所示:

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.Replacer 的速度快了 5 倍

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

【讨论】:

  • 这当然是一个令人印象深刻的性能改进,看起来另一个答案可能会更完美
  • @BrianLeishman 如果您可以将结果直接写入io.Writer,则差异会更大。请参阅答案末尾的编辑。
【解决方案2】:

您可以将\R 模式“解码”为

U+000DU+000A|[U+000AU+000BU+000CU+000DU+0085U+2028U+2029]

查看Java regex docs 解释\R 速记:

换行符匹配器
\R 任何 Unicode 换行序列,等价于 \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

在 Go 中,您可以使用以下内容:

func removeLBR(text string) string {
    re := regexp.MustCompile(`\x{000D}\x{000A}|[\x{000A}\x{000B}\x{000C}\x{000D}\x{0085}\x{2028}\x{2029}]`)
    return re.ReplaceAllString(text, ``)
}

这是Go demo

部分 Unicode 代码可以替换为 Go regexp 支持的正则表达式转义序列:

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

【讨论】:

    猜你喜欢
    • 2016-01-02
    • 2011-01-27
    • 2020-12-09
    • 1970-01-01
    • 2017-10-23
    • 2016-11-13
    • 2022-01-28
    • 1970-01-01
    • 2020-05-08
    相关资源
    最近更新 更多