我正在尝试调试为什么我的 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 = "320264342231240";
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 = "320264342231240";
$VAR1 = "320264342\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çaisb|bЗдоровьеb|bsécuritéb|bsecurityb|bhealthb|bдерьмоb|bfrenchb|bcrapb|bábcb)
$VAR1 = "| (\bx{c0}\ propos\ de\ l\'italie\,\ en\ franx{e7}ais\b|\bx{417}x{434}x{43e}x{440}x{43e}x{432}x{44c}x{435}\b|\bsx{e9}curitx{e9}\b|\bsecurity\b|\bhealth\b|\bx{434}x{435}x{440}x{44c}x{43c}x{43e}\b|\bfrench\b|\bcrap\b|\bx{e1}bc\b)";
请注意,unicode_strings
功能修复了一个可能阻止À
匹配w
的错误。use 5.014;
启用该功能(以及更多功能(。
通常,几乎一发布这个,我就想出了解决方案!所以看起来Perl键不喜欢俄语?我以前从来不需要这样做,所以也许这就是它没有出现的原因。我调整了代码,以便使用带有哈希引用的数组来创建正则表达式:
my $text_repl = '| (';
foreach my $x (@lc_words) {
my $v = quotemeta $x->{word};
$v = 'b' . $v if $v =~ /^w/;
$v .= 'b' if $v =~ /w$/;
$text_repl .= "|$v";
}
$text_repl .= ')';
现在这很完美=(