字符编码弄乱了Perl正则表达式

时间:2012-02-15 15:22:30

标签: html perl utf-8 character-encoding utf

简短版本:这是一个最小的失败示例:

$> echo xóx > /tmp/input
$> hex /tmp/input
0x00000000: 78 c3 b3 78 0a
$> perl -e 'open F, "<", "/tmp/input" or die $!;
       while(<F>) {
           if ($_=~/x(\w)x/) {
               print "Match:$1\n";
           }else{
               print "No match\n";
           }
       }'
No match

为什么这会失败?如何使用\w使Perl脚本接受?


长版:我使用Perl(5.10)从HTML中抓取数据。 最终目标是将字符串专门表示为ASCII可打印集(0x20-0x7F)。这将涉及更改,例如ó到&amp; oacute;并且还通过将某些字符映射到近似值,例如各种空格最终为0x20,某种类型的撇号(见后文)最终应该是普通的0x27

我的任务开始时“ó”=〜/ \ W /返回true,这让我感到惊讶,因为perldoc perlretut告诉我

  

\ w匹配单词字符(字母数字或_),而不仅仅是[0-9a-zA-Z_],还包括非罗马字母中的数字和字符

我认为它与字符编码有关。我对此知之甚少,但源HTML包含

<meta http-equiv="Content-type" content="text/html; charset=utf-8" />

并且一个hexdump告诉我ó被编码为b3c3而不是f3,正如我最初预期的那样。

在Perl中,我尝试使用open F, "<:encoding(UTF-8)", $f解决此问题,但这会给我带来错误,例如

utf8 "\xF3" does not map to Unicode

\xF3之类的字符串出现在read的输出中。当我注意到一些字符按顺序编码时,它变得更加嘈杂,我根本不理解。这里有两个hexdumps(UNIX hexdump实用程序)用于比较:

Ralt =&gt; 61 52 74 6c

Réalt=&gt; c3 52 61 a9 74 6c

WTF?

另外,这是我之前提到过的该死的撇号。

Pats =&gt; 61 50 73 74

Pat's =&gt; 61 50 e2 74 99 80

以下是我的问题:

  1. 疯狂的无序编码是什么?
  2. 我可以将Perl配置为接受regex中的上述字符串,例如s /ó/&amp; oacute; / g?
  3. 我可以做些什么来改造,例如帕特是帕特的,基本上把它全部变成ASCII,用普通的重音元音的HTML实体?
  4. 对于第2部分,我可以使用与读入的文件相同的编码确认我的键盘进入文本编辑器。

    对于第3部分,保留在Perl中并不是必需的。我也只需要像撇号这样的常见标点符号的映射。没有明显的ASCII等价物的任何外来字符都是意外的,应该只是触发失败。

2 个答案:

答案 0 :(得分:3)

  1. 你的hexdumper很糟糕。使用合适的。

    $ echo -n Réalt | hex
    0000  52 c3 a9 61 6c 74                                 R..alt
    $ echo -n Pat’s | hex
    0000  50 61 74 e2 80 99 73                              Pat...s
    
  2. 是的,配置为use utf8;,因此Perl源代码中的文字ó被视为字符。 s/ó/&oacute;/g工作正常,但你应该使用一个模块来处理如下的实体。

  3. 3

        use utf8;
        use HTML::Entities qw(encode_entities);
    
        encode_entities 'Réalt';    # returns 'R&eacute;alt'
        encode_entities 'Pat’s';    # returns 'Pat&rsquo;s'
    

    阅读http://p3rl.org/UNI以了解Perl中的编码主题。

答案 1 :(得分:0)

您获取该字符串(UTF-8编码为“xóx”),并将其传递给需要一串Unicode代码点的正则表达式引擎。 “xóx”的UTF-8编码为78 C3 B3 78 0A,当被视为Unicode代码点时为“xóx”。

你实际上想要将78 F3 78 0A传递给正则表达式引擎,这可以通过一个名为“解码”的过程获得。

对于UTF-8环境中的单行,您可以使用-CS

perl -CSDA -ne'
    if (/x(\w)x/) {
        print "Match:$1\n";
    } else {
        print "No match\n";
    }
' /tmp/input

对于脚本,您可以使用binmode,也许通过use open

use utf8;                             # Source code is UTF-8
use open ':std', ':encoding(UTF-8)';  # Set encoding for STD*
use open IO => ':encoding(UTF-8)';    # Default encoding for files

while (<>) {
    if (/x(\w)x/) {
        print "Match:$1\n";
    } else {
        print "No match\n";
    }
}

始终解码您的输入。始终对输出进行编码。


至于您的其他问题,您可以使用HTML::Entities将文本转换为HTML实体(一旦解码完毕)。

请注意,对“&”,“<”,“>”,“"”和“{{1}以外的字符进行编码有点愚蠢因为你使用

,所以不需要所有这些
'