这是一个简单的替换,它在unicode字符串中围绕大写字符添加括号。如您所见,结果相当丑陋:
~$ echo "Whatéver 5" | perl -ape "s/(\p{Upper})/(\1)/g"
(W)hat(�)�ver 5
我的理解是正则表达式操作的是“代码点”而不是“逻辑字符”,它将我的“é”分成无意义的字符。有没有办法强制正则表达式一次处理逻辑unicode字符?
谢谢,
答案 0 :(得分:2)
你没有告诉Perl期望UTF-8输入,所以它将编码的每个字节视为一个单独的字符
在程序中,您可以设置三个标准IO通道的默认编码,如
use open ':std' => ':encoding(UTF-8)'
在命令行中,选项-CS
执行相同的操作,因此这应该适合您。我删除了不必要的autosplit选项,并将\1
替换为替换字符串中的正确$1
echo "Whatéver 5" | perl -CS -pe "s/(\p{Upper})/($1)/g"
答案 1 :(得分:2)
正如其他答案所示,在Perl中打开UTF-8是一个零碎的过程。语法和原始字符串有use utf8
。然后你必须确保所有的文件句柄都是UTF-8。那么@ARGV
呢? readdir
? glob
? ``
的输出?
没有什么比让一半程序使用ASCII而另一半使用UTF-8更糟糕了。 utf8::all救援!
安装它,添加use utf8::all
,它将打开UTF-8 ...所有这些。别人想通了,你不用担心。
$ echo "Whatéver 5" | perl -ape "use utf8::all; s/(\p{Upper})/(\1)/g"
(W)hatéver 5
答案 2 :(得分:1)
假设您的终端使用UTF-8编码,
$ echo -n "é" | perl -ne 'printf "%vX\n", $_'
给出
C3.A9
所以Perl程序的输入内部没有转换为Unicode(它仍然是一个UTF-8字节的字符串)
要将输入转换为Perl字符串,请使用选项-CI
在标准输入流上添加UTF-8图层:
$ echo -n "é" | perl -CI -ne 'printf "%vX\n", $_'
输出现在是
E9
但是,如果您还尝试将字符打印回标准输出
你不会从终端获得é
,而是获得unicode替换字符�
。这是因为字符0xE9
是Unicode,但终端期望UTF-8,0xE9
无效UTF-8:
$ echo -n "é" | perl -CI -nE 'printf "$_: %vX\n", $_, $_'
�: E9
要获得正确的输出,您还可以在标准输出流上添加UFT-8编码图层(使用-CO
标志):
$ echo -n "é" | perl -CIO -nE 'printf "$_: %vX\n", $_, $_'
é: E9
“Upper”是“大写”的同义词,我们可以写 \ p {大写}等价于\ p {Upper}
和
例如,\ p {Uppercase}匹配任何单个字符 Unicode“大写”属性
如果您尝试在字节字符串上使用\p{Upper}
,您将不会收到来自Perl的任何警告。 0xC0
到0xDE
范围内的字节也将匹配大写属性。尝试
perl -E 'for $i (0x80..0xFF) {$_=chr $i; printf "%x\n", $i if /\p{Upper}/}'
这解释了你得到的输出:
$ echo "Whatéver 5" | perl -ape "s/(\p{Upper})/(\1)/g"
(W)hat(�)�ver 5
此处,字母é
表示为2个字节(UTF-8)0xC3
和0xA9
,0xC3
将与Unicode Upper
匹配属性。
因此,您的问题的解决方案是在标准输入和输出上添加UTF-8编码图层(您可以使用-CI
合并-CO
和-CS
):
echo "Whatéver 5" | perl -CS -ape "s/(\p{Upper})/(\1)/g"
带输出:
(W)hatéver 5