R中的正则表达式:gsub模式

5
我正在学习R语言的正则表达式,但是我不太理解这个 gsub 的例子:
gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\\\1", x)

到目前为止,我理解的是:

  • 如果x是字母数字,则不匹配,因此不做任何修改
  • 如果x包含 . |({}+$?,则在其前面添加\\

我无法解释:

> gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\\\1", '10\1')
[1] "10\001"

或者
> gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\\\1", '10/1')
[1] "10/1"

我也不明白为什么替换字符串"\\\\\\1"只添加了两个括号。

我应该弄清楚这个函数的作用,我认为它应该转义某些特殊字符吗?

2个回答

6
整个模式被括号包裹,这允许回溯引用。其中的一部分:
[.|()\\^{}+$*?]

... 是一个“字符类”,因此它匹配方括号内的任何字符之一,并且正如您所说,它正在更改模式语法将在模式定义中本应为元字符的方式进行解释。

接下来是一个“管道”字符,它是regex-OR,后面是一个转义的开放方括号,另一个“OR”管道,然后是一个转义的关闭方括号。由于R和regex都使用反斜杠作为转义符,因此需要将它们加倍以获取模式中的R+ regex-escape,但在替换字符串中则不需要。 如果要在字符类中输入关闭方括号,则必须将其放置在字符串的开头,因此整个模式可以更紧凑地形成:

 "[][.|()\\^{}+$*?]"  # without the "|\\[|\\])"

在替换字符串中,“\\n”表示匹配'pattern'的第n个括号部分,此处'\1'是替换的第二部分。第一位置“\”是转义符,第二个“\”是反斜杠。现在准备好迎接更奇怪的部分……这个结果有多少个字符?
> nchar( gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\1", '10\1') )
[1] 3

当然,匹配项中没有任何一个等于'\1'。在你之前写教程的人(我认为不是gsub帮助页面)有一种奇怪的幽默感。以下是一些函数,如果您需要创建可能被系统readline函数拦截的字符,则可能会有用:

> intToUtf8(1)
[1] "\001"
> ?intToUtf8
> 0x0
[1] 0
> intToUtf8(0)
[1] ""
> utf8ToInt("")
integer(0)

请查看?Quotes,在这里可以找到大量有用的信息(标题可能不太合适),涉及R如何处理八进制、十六进制和其他数字以及特殊字符。


我无法让你提供的gsub替换模式起作用 - gsub("([][.|()\\^{}+$*?])","\\1","[ ]")会报错。 - thelatemail
同意。这是成功的:"[][|(.)\\^{}+$*?]"。与方括号内元字符的顺序有关。 gsub("([][|(.)\\^{}+$*?])", "hit", "]") #[1] "hit" - IRTFM
太棒了!但是你能解释一下 "\\\\1""\\\\\\1" 的区别吗?我不确定我理解了。 - will resal
嗯,既然“模式”没有匹配任何内容,我不确定该解释什么。 - IRTFM
@BondedDust,这里的区别在于:> gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\\\1", '10.1') [1] "10\\.1" > gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\1", '10.1') [1] "10\\11"是我原来问题中的内容,而不是你的答案。 - will resal
那不是原始问题中的内容。替换有两个有效组成部分(因为现在有匹配了):(a)实际上变成转义和反斜杠字符的前4个反斜杠,然后(b)第5个和第6个反斜杠及其与“1”的反向引用。与由“\ 1”引用的反向引用内部材料相匹配的项是一个句点。实际上结果中只有5个字符。 - IRTFM

4
第一个被拆解的正则表达式如下:
 (                             # (1 start)
      [.|()\^{}+$*?] 
   |  \[
   |  \]
 )                             # (1 end)

它捕获了在'class','['或']'中的任何内容,然后看起来用\\\\\1替换它,这是一个转义加上捕获1中的任何内容。
所以,基本上它只是转义其中一个字符的单个出现。
正则表达式可以更好地编写为([.|()^{}\[\]+$*?]),或者在字符串中为"([.|()^{}\\[\\]+$*?])" 编辑(推广评论) -
正则表达式不会匹配字符串10\1,因此不应该有替换。打印输出时必须进行插值(语言)。看起来它将其转换为八进制数\001。-由于它无法显示二进制1,因此它显示其八进制等效形式。

我认为"10\1"也存在一些(可能是无意的)奇怪之处,它转义了数字1 - 例如,cat("10\1")只会返回10 - 从快速谷歌搜索中得知,\1\001是一个空字符。 - thelatemail
@thelatemail - 正则表达式不会匹配字符串10\1,因此不应进行替换。打印输出时可能存在插值(语言)问题。看起来它将其转换为八进制\001。- 由于它无法显示1,因此显示其八进制等价物。 - user557597
@BondedDust - 我需要一张表格来找到那个。 - user557597
@BondedDust - 是的,我的确没有幽默感。 - user557597
@sln "正则表达式可以更好地编写为..." 在这里不起作用,但其余部分有帮助! - will resal
关于元字符在字符类内部的顺序如何影响结果,有一些奇怪的地方(或者至少在R的正则表达式帮助页面中没有记录)。请参见我的答案下面的评论中的替代方法。 - IRTFM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接