go 正则表达式:扩展规则令人困惑

vjrehmav  于 8个月前  发布在  Go
关注(0)|答案(6)|浏览(74)

你正在使用的Go版本是什么(go version)?

$ go version
go version go1.14.4 darwin/amd64

Also tested on Go docker containers: 1.2, 1.10

这个问题在最新版本的Go中是否重现?

你正在使用的操作系统和处理器架构是什么(go env)?
go env 输出

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/adrian/Library/Caches/go-build"
GOENV="/Users/adrian/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/adrian/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/nix/store/6f0x9mycyrcc3gsfm36bci0b4ls1q8zc-go-1.14.4/share/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/nix/store/6f0x9mycyrcc3gsfm36bci0b4ls1q8zc-go-1.14.4/share/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/zz/yrqp3xx513sf6drry24cw8r40000gp/T/go-build152779906=/tmp/go-build -gno-record-gcc-switches -fno-common"

你做了什么?
https://play.golang.org/p/WYmtk1Gi-T_4
https://play.golang.org/p/XAdw3-cgx0z

package main

import (
	"fmt"
	"regexp"
)

func main() {
	re := regexp.MustCompile(`a(.)c`)
	fmt.Println(re.ReplaceAllString("abc", "c$1a"))
	// expected "cba", got "c"
}

你期望看到什么?
根据https://golang.org/pkg/regexp/#Regexp.ReplaceAllString的文档,预期第一个捕获子匹配(.)会导致字符b,因此结果应该是cba
ReplaceAllString返回src的副本,用替换字符串repl替换正则表达式匹配项。在repl中,$符号按照Expand中的解释方式解释,例如$1表示第一个子匹配的文本。
你实际上看到了什么?
我只得到了字符c,我期望得到的是cba,因为替换被以不同的方式解释了。
当我深入调查以提出这个报告时,我在Expand函数的文档中找到了这个:
在$name形式中,name被尽可能长地解释为:$1x等同于${1x},而不是${1}x,而且,$10等同于${10},而不是${1}0。
我没有意识到我正在使用name形式,我认为我正在使用number形式。这使得更清楚地看出正则表达式组被解释为${1a},而不是$1,后面跟着a
这意味着,我需要使用"c${1}a"作为我的替换,这非常不寻常,因为我还没有见过其他编程语言是这样工作的,例如Node.js将返回cba

"abc".replace(/a(.)c/, "c$1a");

sed也符合我的预期(尽管它使用\1作为引用而不是$1)。

echo "abc" | sed -E 's/a(.)c/c\1a/g'

对我来说,这是令人惊讶的行为,我在意识到有其他事情发生之前花了一段时间调试我正在工作的程序。
理想情况下,Go实现应该改变以匹配其他编程语言和文本编辑器,即在美元之后,如果第一个字符是[0-9],那么它是直到下一个非数字字符的数字引用,例如

$1 = $1
$1a = $1, followed by a
${1a} = ${1a}
$10 = $10
$1_ = $1, followed by _
a$1c = a, $1, c
etc.

然而,如果不可能的话,那么文档更改可能会有所帮助。 $1是不稳定的,它只在你对捕获组的引用是整个替换( $1 ),或者在替换的末尾跟有一个空格字符( $1 a )等的情况下才起作用。因此,文档应该默认使用长格式。

up9lanfz

up9lanfz1#

我将这个标记为文档,因为我不确定在不破坏实际程序的情况下如何更改它。我也曾经被这个问题困扰过。

vulvrdjw

vulvrdjw2#

谢谢。这不是我们今天可以改变的事情,所以它必须是一个文档修复。

9cbw7uwe

9cbw7uwe3#

https://golang.org/cl/244625提到了这个问题:doc: improve re.ReplaceAllString documentation related to Expand part

ghg1uchk

ghg1uchk4#

我认为最简单的改变是引导人们使用更明确的语法。很多时候,人们并不查看文档中的示例,他们只是查看在他们的开发环境中弹出的文本,那么这个怎么样?
ReplaceAllString函数返回src的一个副本,用替换字符串repl替换Regexp的匹配项。在repl中,$符号被解释为Expand中的含义,例如,${1}表示第一个子匹配的文本,${xyz}表示名为xyz的子匹配的文本。
在引用子匹配时跳过括号的问题在示例代码中有所暗示,但它有点太微妙了,也许在那里添加一个注解会让它引起注意。我认为不需要做更多的改动。

// $1W is not a named submatch, so the replacement is with an empty slice.
fmt.Printf("%s\n", re.ReplaceAll([]byte("-ab-axxb-"), []byte("$1W")))
wlzqhblo

wlzqhblo5#

将PR重置到当前 Backbone.js ,我们能再审查一次并完成吗?

y4ekin9u

y4ekin9u6#

https://go.dev/cl/446836提到了这个问题:regexp: improve ReplaceAllString documentation related to Expand part

相关问题