我需要在我的Go代码中匹配使用反向引用(例如\1)的正则表达式。
这并不容易,因为在Go中,官方regexp包使用RE2引擎,它选择不支持反向引用(以及一些其他不太常见的功能),以便可以保证线性时间执行,从而避免正则表达式拒绝服务攻击。启用RE2的反向引用支持不是一个选项。
在我的代码中,没有受到攻击者恶意利用的风险,我需要反向引用。
我该怎么办?
我需要在我的Go代码中匹配使用反向引用(例如\1)的正则表达式。
这并不容易,因为在Go中,官方regexp包使用RE2引擎,它选择不支持反向引用(以及一些其他不太常见的功能),以便可以保证线性时间执行,从而避免正则表达式拒绝服务攻击。启用RE2的反向引用支持不是一个选项。
在我的代码中,没有受到攻击者恶意利用的风险,我需要反向引用。
我该怎么办?
在这里回答自己的问题,我使用了golang-pkg-pcre来解决这个问题。它使用libpcre++和支持反向引用的perl正则表达式。它的API并不相同。
当我遇到同样的问题时,我使用了一个两步正则表达式匹配来解决它。原始代码是:
if m := match(pkgname, `^(.*)\$\{DISTNAME:S(.)(\\^?)([^:]*)(\\$?)\2([^:]*)\2(g?)\}(.*)$`); m != nil {
before, _, left, from, right, to, mod, after := m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]
// ...
}
这段代码的作用是解析形如${DISTNAME:S|from|to|g}
的字符串,它本身是一种使用熟悉的替换语法S|replace|with|
的小型模式语言。
这个两阶段的代码看起来像这样:
if m, before, sep, subst, after := match4(pkgname, `^(.*)\$\{DISTNAME:S(.)([^\\}:]+)\}(.*)$`); m {
qsep := regexp.QuoteMeta(sep)
if m, left, from, right, to, mod := match5(subst, `^(\^?)([^:]*)(\$?)`+qsep+`([^:]*)`+qsep+`(g?)$`); m {
// ...
}
}
match
,match4
和match5
是我对regexp
包的封装,它们会缓存已编译的正则表达式,以便至少不会浪费编译时间。
我认为这是一个老问题了,但我从以上答案中没有找到简单的解决方案。
此外,“golang-pkg-pcre”在使用M1的macOS上无法使用。
因此,我想提出我的想法。
例如,将<u>或<I>替换为<b>,并用</u>或</I>替换为</b>。搜索不区分大小写。
让我比较一下如何在Python和Go中实现
在Python中,只需以下简单代码即可:
import re
content = "<u>test1</u> <i>test2</i>\n<U>test3</U> <I>test4</I>"
content = re.sub(r"<(u|i)>([^<>]+?)</\1>", r"<b>\2</b>", content, flags=re.IGNORECASE)
print(content)
package main
import (
"fmt"
"regexp"
)
func main() {
content := "<u>test1</u> <i>test2</i>\n<U>test3</U> <I>test4</I>"
content = changeUITagToBTag(content)
fmt.Println(content)
}
// change <u> or <i> to <b> and </u> or </i> to </b>
// case-insensitive search
func changeUITagToBTag(content string) string {
pattern := `<(u|i)>([^<>]+?)</(u|i)>`
compiledPattern := regexp.MustCompile(fmt.Sprintf(`(?%v)%v`, "i", pattern))
content = compiledPattern.ReplaceAllStringFunc(content, func(text string) string {
allSubStrings := compiledPattern.FindAllStringSubmatch(text, -1)
if allSubStrings[0][1] == allSubStrings[0][3] {
return fmt.Sprintf(`<b>%s</b>`, allSubStrings[0][2])
}
return text
})
return content
}
正则表达式包的函数 FindSubmatchIndex 和 Expand 可以通过反向引用来捕获内容。虽然不是很方便,但仍然是可能的。示例
package main
import (
"fmt"
"regexp"
)
func main() {
content := []byte(`
# comment line
option1: value1
option2: value2
# another comment line
option3: value3
`)
pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)
template := []byte("$key=$value\n")
result := []byte{}
for _, submatches := range pattern.FindAllSubmatchIndex(content, -1) {
result = pattern.Expand(result, template, content, submatches)
}
fmt.Println(string(result))
}
输出
option1=value1
option2=value2
option3=value3