Golang替换所有换行符

时间:2018-10-01 15:08:18

标签: regex string go

通常,当我替换换行符时,我会跳到Regexp,就像在这个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中的(?:(?>\r\n)|\v),但看起来Go的正则表达式实现也不支持,invalid or unsupported Perl syntax: '(?>'恐慌

在Go和Regex中替换换行符的一种好而安全的方法是什么?


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

2 个答案:

答案 0 :(得分:3)

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

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

请参阅Java regex docs来解释\R的简写:

Linebreak matcher
\R  Any Unicode linebreak sequence, is equivalent to \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}]`)

答案 1 :(得分:3)

虽然使用regexp通常会产生一种优雅而紧凑的解决方案,但它通常不是最快的。

对于必须将某些子字符串替换为其他子字符串的任务,标准库以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'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")
}

实现实际上非常快。这是一个简单的基准,将其与上述预编译的regexp解决方案进行比较:

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响应或将结果写入文件),则可以避免在出现{{的情况下,不必创建新的string 1}},因为它有一个方便的Replacer.WriteString()方法,该方法接受一个strings.Replacer并将结果写入其中,而无需以io.Writer的形式分配和返回。与正则表达式解决方案相比,这可以进一步显着提高性能。