Perl中的正则表达式无法处理特定的特殊字符。

6

我无法从字符串中去除特殊字符¤:

$word = 'cɞi¤r$c❤u¨s';
$word =~ s/[^a-zöäåA-ZÖÄÅ]//g;
printf "$word\n";

在第二行,我尝试从字符串$word中删除任何非字母字符。我期望得到单词circus的输出结果,但实际上输出了:

ci�rc�us

表达式中的öäåÖÄÅ只是瑞典字母表中的普通字符,我需要包含它们。


你正在使用哪个 Perl 版本?Unicode 支持逐渐被添加和完善。尝试使用至少 5.12,并查看 choroba 的答案。使用 perl -v 命令打印版本。Perl Unicode 手册 - cfi
有经验的程序员为什么总是改变问题?现在我的(低)经验用户永远找不到他的问题的答案。 - Pithikos
我也觉得编辑过于激进了。不过现在加入Unicode标签是好的。 - cfi
3个回答

11

如果字符在您的源代码中,请确保使用 utf8。 如果它们是从文件中读取的,请使用binmode $FILEHANDLE, ':utf8'

请务必阅读 perldoc perlunicode


我认为编码是正确的,因为我可以毫无问题地运行 printf 'cɞi¤r$c❤u¨söäå';。但当我运行正则表达式时出现了问题。从正则表达式中删除 öäå 可以解决问题,但这样我就不能在字符串中使用这些字符 :/ - Pithikos
1
即使没有使用 use utf8,您仍然可以打印字符串,但在这种情况下,Perl 打印的是 字节 而不是 _字符_。这也是为什么它无法识别正则表达式中的字符。您读过 perlunicode 吗? - choroba

3

简短回答:添加"use utf8;"语句,确保源代码中的文字字符串被解释为utf8编码,包括测试字符串和正则表达式的内容。

详细回答:

#!/usr/bin/env perl

use warnings;
use Encode;

my $word = 'cɞi¤r$c❤u¨s';

foreach my $char (split //, $word) {
    print ord($char) . Encode::encode_utf8(":$char ");
}

my $allowed_chars = 'a-zöäåA-ZÖÄÅ';

print "\n";

foreach my $char (split //, $allowed_chars) {
    print ord($char) . Encode::encode_utf8(":$char ");
}

print "\n";

$word =~ s/[^$allowed_chars]//g;

printf Encode::encode_utf8("$word\n");

没有使用utf8执行:

$ perl utf8_regexp.pl
99:c 201:É 158: 105:i 194:Â 164:¤ 114:r 36:$ 99:c 226:â 157: 164:¤ 117:u 194:Â 168:¨ 115:s 
97:a 45:- 122:z 195:Ã 182:¶ 195:Ã 164:¤ 195:Ã 165:¥ 65:A 45:- 90:Z 195:Ã 150: 195:Ã 132: 195:Ã 133: 
ci¤rc¤us

使用UTF8执行:

$ perl -Mutf8 utf8_regexp.pl
99:c 606:ɞ 105:i 164:¤ 114:r 36:$ 99:c 10084:❤ 117:u 168:¨ 115:s 
97:a 45:- 122:z 246:ö 228:ä 229:å 65:A 45:- 90:Z 214:Ö 196:Ä 197:Å 
circus

解释:

您在源代码中输入的非ascii字符由一个或多个字节表示。由于您的输入是utf8编码的,在纯ascii或latin-1终端中,这些字符将只占用一个字节。

当不使用utf8模块时,perl认为您输入的每个字节都是一个单独的字符,就像在拆分并打印每个单独的字符时所看到的那样。使用utf8模块时,它根据utf8编码规则正确地将几个字节组合成一个字符。

可以看到,令人巧合的是,一些瑞典字符由某些测试字符串中的一些字符由的字节匹配,并且被保留下来。即:ö在utf8中由195:Ã 164:¤组成 - 164最终成为您允许的一个字符,并通过了字符串验证。

解决方案是告诉perl你的字符串应该被视为utf-8。

encode_utf8调用的目的是避免警告将宽字符打印到终端。始终需要根据输入或输出应处理/操作的字符编码进行解码输入和编码输出。

希望这样更加清晰易懂。


抱歉,我的回答与其他人重复了。如果您想的话,可以删除或者点踩这个回答;-/ - nicomen

-8

choroba所指出的, 将这段代码添加到perl脚本开头即可解决问题:

use utf8;
binmode(STDOUT, ":utf8");

use utf8让你可以在正则表达式中正确使用特殊字符,而binmode(STDOUT, ":utf8")则让你在 shell 中正确输出特殊字符。


3
这就是choroba已经建议的。你为什么要提供他答案的拷贝?相反,应该给choroba“答案”的奖励(并点赞他)(然后删除你自己的回答)。 - cfi
因为我的回答更加实用。我并不试图与他竞争,而是尽力为将来遇到同样问题的用户详细解释。在我看来,回答越多越好。 - Pithikos
2
@Pithikos,如果答案对你有所帮助,你可以将它作为补充评论添加到他的答案中。 - Qtax
当他的评论比他的回答更冗长时,这只是一个小注释而已。我不明白为什么要大惊小怪。他的回答对我来说还不够满意。我仍然没有解决我的问题,所以我不明白为什么我被强迫接受它。 - Pithikos
3
你的回答提出了与choroba已经向你指出的完全相同的两个观点。可以说,choroba的回答值得修改,你可以在评论中提及这一点。但是,声明他的回答为你的回答是错误的。顺便说一句,在你自己的回答中,你声称“这样做...解决了问题”,并重复了choroba的代码——这就是我认为他应该获得答案勾选标记的原因。 - cfi
我没有将他的答案声明为我的。如果我有权限,我会编辑他的答案并接受它。然而,对于像我这样的初学者来说,很难理解他在谈论什么。我甚至不明白use utf8是一个代码片段。在我看来,他是在让我使用utf8,但我不知道如何“使用utf8”,因此无法将其翻译成任何有意义的内容。期望初学者理解你的行话并不是一种教学方法。 - Pithikos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接