我对以下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
答案 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=TRUE
或useBytes=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"