utf8似乎打破了Perl中的正则表达式规则?

安德鲁·纽比(Andrew Newby)

我正在尝试调试为什么我的UTF-8在脚本中无法正常工作。这是原始代码:

$lc_custom{"À propos de l'italie, en français"} = "foo bar";
$lc_custom{"Здоровье"} = "foo bar";
$lc_custom{"дерьмо"} = "foo bar";
$lc_custom{"sécurité"} = "foo bar";
$lc_custom{"security"} = "foo bar";
$lc_custom{"health"} = "foo bar";
$lc_custom{"french"} = "foo bar";
$lc_custom{"ábc"} = "foo bar";
$lc_custom{"crap"} = "foo bar";

my $text_repl = '| (' . join('|', map { my $v = quotemeta; $v = '\b'.$v if $v =~ /^\w/; $v .= '\b' if $v =~ /\w$/ } sort { length($b) <=> length($a) } keys %lc_custom) . ')';

我得到的调试:

$VAR1 = {
          'foo' => '| (\\�\\�\\ propos\\ de\\ l\\\'italie\\,\\ en\\ fran\\�\\�ais\\b||||\\bsecurity\\b|\\bhealth\\b|\\bfrench\\b|\\�\\�bc\\b|\\bcrap\\b)'
        };

这是我的修订版,其中包含更多调试信息:

my $text_repl = '| (' . join('|', map {
    print "FOO BAR: $_ \n";
    my $v = $_;
    $v = '\b' . $v if $v =~ /^\w/;
    $v .= '\b' if $v =~ /\w$/
} sort { length($b) <=> length($a) } keys %lc_custom) . ')';

我得到:

FOO BAR: À propos de l'italie, en français 
FOO BAR: Здоровье 
FOO BAR: дерьмо 
FOO BAR: sécurité 
FOO BAR: security 
FOO BAR: health 
FOO BAR: french 
FOO BAR: ábc 
FOO BAR: crap 

$VAR1 = {
          'foo' => '| (\\QÀ propos de l\'italie, en français\\E\\b||||\\b\\Qsecurity\\E\\b|\\b\\Qhealth\\E\\b|\\b\\Qfrench\\E\\b|ábc\\E\\b|\\b\\Qcrap\\E\\b\\E)'
        };        

带有俄语时,似乎所有按键都不喜欢工作。会有什么原因吗?

更新:根据要求,使用以下代码是这样的:

use utf8;
my $test = '| (' . join('|', map { my $v = quotemeta; $v = '\b'.$v if $v =~ /^\w/; $v .= '\b' if $v =~ /\w$/ } sort { length($b) <=> length($a) } keys %lc_custom) . ')';
use Data::Dumper;
$Data::Dumper::Useqq = 1;
print Dumper({ BLA => $test });

给出:

"BLA" => "| (\\\303\\\200\\ propos\\ de\\ l\\'italie\\,\\ en\\ fran\\\303\\\247ais\\b||||\\bsecurity\\b|\\bhealth\\b|\\bfrench\\b|\\\303\\\241bc\\b|\\bcrap\\b)"
池上

解码您的输入;编码您的输出。问题源于缺乏前者。的键%lc_custom是使用UTF-8编码的文本字符串。通常,您不想使用编码的文本。您想使用解码后的文本。

无论quotemeta\w正则表达式字符类希望提供解码的文本。将编码的文本传递给他们没有任何意义。但这就是您正在做的。


让我们看一个简单的例子。

use Data::Dumper qw( Dumper );
$Data::Dumper::Useqq = 1;

# "д♠" encoded using UTF-8 (encoded text).
my $utf8 = "\320\264\342\231\240";
say length($utf8);
print Dumper($utf8);
print Dumper(quotemeta($utf8));
say length(quotemeta($utf8));

say "";

# "д♠" as decoded text (Unicode Code Points).
my $ucp = "\x{434}\x{2660}";
say length($ucp);
print Dumper($ucp);
print Dumper(quotemeta($ucp));
say length(quotemeta($ucp));
5
$VAR1 = "\320\264\342\231\240";
$VAR1 = "\320\264\342\\\231\\\240";
7

2
$VAR1 = "\x{434}\x{2660}";
$VAR1 = "\x{434}\\\x{2660}";
3

请注意,quotemeta($utf8)在“♠”的编码中间插入了2个反斜杠,在其之前没有一个反斜杠。另一方面,quotemeta($ucp)在两个字符之间添加了一个反斜杠。

简而言之,您将垃圾传递给quotemeta,而您又将垃圾带回。


Perl期望其源代码使用ASCII编码,除非您通过使用告诉它使用UTF-8编码use utf8;

use 5.014;      # Or: use strict; use feature qw( say unicode_strings );
use warnings;

# Tell Perl the source code is encoded using UTF-8.
use utf8;

# Tell Perl the terminal provides/expects UTF-8.
# Also sets the default for `open`.
use open ':std', ':encoding(UTF-8)';

use Data::Dumper qw( Dumper );
$Data::Dumper::Useqq = 1;

# From the question, verbatim.
my %lc_custom;
$lc_custom{"À propos de l'italie, en français"} = "foo bar";
$lc_custom{"Здоровье"} = "foo bar";
$lc_custom{"дерьмо"} = "foo bar";
$lc_custom{"sécurité"} = "foo bar";
$lc_custom{"security"} = "foo bar";
$lc_custom{"health"} = "foo bar";
$lc_custom{"french"} = "foo bar";
$lc_custom{"ábc"} = "foo bar";
$lc_custom{"crap"} = "foo bar";

# From the question, verbatim.
my $text_repl = '| (' . join('|', map { my $v = quotemeta; $v = '\b'.$v if $v =~ /^\w/; $v .= '\b' if $v =~ /\w$/ } sort { length($b) <=> length($a) } keys %lc_custom) . ')';

say $text_repl;
print Dumper($text_repl);

输出:

| (\bÀ\ propos\ de\ l\'italie\,\ en\ français\b|\bЗдоровье\b|\bsécurité\b|\bsecurity\b|\bhealth\b|\bдерьмо\b|\bfrench\b|\bcrap\b|\bábc\b)
$VAR1 = "| (\\b\x{c0}\\ propos\\ de\\ l\\'italie\\,\\ en\\ fran\x{e7}ais\\b|\\b\x{417}\x{434}\x{43e}\x{440}\x{43e}\x{432}\x{44c}\x{435}\\b|\\bs\x{e9}curit\x{e9}\\b|\\bsecurity\\b|\\bhealth\\b|\\b\x{434}\x{435}\x{440}\x{44c}\x{43c}\x{43e}\\b|\\bfrench\\b|\\bcrap\\b|\\b\x{e1}bc\\b)";

请注意,此unicode_strings功能修复了可能阻止À匹配的错误\wuse 5.014;启用该功能(以及更多功能)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章