R如何处理正则表达式中的特殊字符?

时间:2014-05-12 13:29:06

标签: regex r

我对以下3项测试的输出感到困惑:

这个包括一个特殊字符«°»并给出了良好的结果:

sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "A°C 01160")
[1] "01160"

这个包括引用并给出了好结果:

sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "01160 'aa")
[1] "01160"

但是这个包括°和引用并返回一个奇怪的结果

sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "A°C 01160 'aa")
[1] "0 'aa"

顺便说一句,如果我给出与向量相同的输入,结果也不一样,我也感到困惑:

sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = c("A°C 01160", "01160 'aa", "A°C 01160 'aa"))
[1] "01160" "0 'aa" "0 'aa"

有没有人知道我的问题的根源?

我在Mac OS 10.8上使用法语UTF-8编码选项运行R 3.02:

> sessionInfo()
R version 3.0.2 (2013-09-25)
Platform: x86_64-apple-darwin10.8.0 (64-bit)

locale:
[1] fr_FR.UTF-8/fr_FR.UTF-8/fr_FR.UTF-8/C/fr_FR.UTF-8/fr_FR.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] tools_3.0.2

3 个答案:

答案 0 :(得分:4)

对包括[:digit:]在内的命名字符类的解释取决于所讨论的语言环境。它们可以包含非ASCII字符。

[[:digit:]]将匹配Unicode Nd 类别中的任何字符。

如果您只想匹配ASCII十进制数字,请使用[0-9]

> sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "A°C 01160 'aa")
[1] "0 'aa"
> sub(pattern = ".*([0-9]{5}).*", replacement = "\\1", x = "A°C 01160 'aa")
[1] "01160"
> 

此外,您的观察并非真正针对R。引自regex

  

某些命名的字符类是预定义的。其   解释取决于语言环境(参见locales);解释   下面是POSIX语言环境。


编辑:演示上面提到的内容:

> Sys.getlocale()
[1] "LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=en_US.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=en_US.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US.UTF-8;LC_IDENTIFICATION=C"
> sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "A°C 01160 'aa")
[1] "0 'aa"
> Sys.setlocale("LC_ALL", "C") 
[1] "LC_CTYPE=C;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=en_US.UTF-8;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US.UTF-8;LC_IDENTIFICATION=C"
> sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "A°C 01160 'aa")
[1] "01160"
> 

为了详细说明演示,相同的替换为不同的语言环境返回了不同的结果。当切换到C区域设置时,结果是符合预期

答案 1 :(得分:3)

您的测试未完全定义问题。问题特别与°字符和所有其他特殊UTF字符有关,例如Ĉ也会导致同样的问题。

根据输入模式,奇怪的输出实际上总是输入字符串的最后五个字符。所以,在你的初始"好"添加另一个角色测试将显示该测试也给出了错误的结果:

sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", x = "A°C 01160a")
[1] "1160a"

引用并不能解决问题,而且是一个红鲱鱼。使用perl=TRUEuseBytes=TRUE也可以防止问题发生。

我认为这个问题与以下摘录有关?regexp:

 In UTF-8 mode the named character classes only match ASCII
 characters

因此,预定义的字符类可能无法正确处理UTF-8文本。在这种情况下,[0-9]代替[[:digit:]]似乎工作正常,因为它不是预定义的类。

尽管如此,我仍然不知道是什么原因造成了最后5个字符的特定输出。我的猜测是,当字符串是UTF-8编码时,预定义的类匹配所有内容,因为您可以使用模式".*(.{5}).*"获得相同的输出。但至少我们可以更好地了解问题的确切原因:预定义的字符类处理UTF-8字符集。

答案 2 :(得分:2)

尝试使用perl = TRUE

> sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", 
      x = "A°C 01160 'aa",perl = TRUE)
[1] "01160"

似乎也适用于其他版本:

> sub(pattern = ".*([[:digit:]]{5}).*", replacement = "\\1", 
    x = c("A°C 01160", "01160 'aa", "A°C 01160 'aa"),perl = TRUE)
[1] "01160" "01160" "01160"