【问题标题】:Perl regex replacement of logical unicode charactersPerl 正则表达式替换逻辑 unicode 字符
【发布时间】:2016-09-02 15:23:02
【问题描述】:

这是一个简单的替换,它在 unicode 字符串中的大写字符周围添加括号。如您所见,结果相当丑陋:

~$ echo "Whatéver 5" | perl -ape "s/(\p{Upper})/(\1)/g"
(W)hat(�)�ver 5

我的理解是,正则表达式对“代码点”而不是“逻辑字符”进行操作,这会将我的“é”拆分为无意义的字符。有没有办法强制正则表达式同时处理逻辑 unicode 字符?

谢谢,

【问题讨论】:

标签: perl unicode


【解决方案1】:

如其他答案所示,在 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】:

    你没有告诉 Perl 期待 UTF-8 输入,所以它把编码的每个字节当作一个单独的字符

    在程序中,您可以像这样为三个标准 IO 通道设置默认编码

    use open ':std' => ':encoding(UTF-8)'
    

    在命令行上,选项-CS 做同样的事情,所以这应该适合你。我删除了不必要的自动拆分选项,并在替换字符串中用正确的$1 替换了\1

    echo "Whatéver 5" | perl -CS -pe "s/(\p{Upper})/($1)/g"
    

    【讨论】:

      【解决方案3】:

      假设您的终端使用 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
      

      根据perlunicode

      "Upper" 是 "Uppercase" 的同义词,我们可以这样写 \p{Uppercase} 等价于 \p{Upper}

      例如,\p{Uppercase} 匹配任何带有 Unicode“大写”属性

      似乎如果您尝试在字节字符串上使用\p{Upper},您将不会收到来自 Perl 的任何警告。 0xC00xDE 范围内的字节也将匹配大写属性。试试

      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 中)0xC30xA90xC3 将匹配 Unicode Upper 属性。

      因此,解决您的问题的方法是在标准输入和输出上添加 UTF-8 编码层(您可以使用 -CS 组合 -CI-CO):

      echo "Whatéver 5" | perl -CS -ape "s/(\p{Upper})/(\1)/g"
      

      有输出:

      (W)hatéver 5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-11
        • 1970-01-01
        • 1970-01-01
        • 2014-02-01
        • 1970-01-01
        • 2022-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多