在Perl中,如何匹配Unicode字符串中仅为完整字符的字符?

8
我正在寻找一种方法,只匹配Unicode字符串中完整组成的字符。
在任何包含此字符类的正则表达式实现中,[:print:]是否依赖于语言环境?例如,它是否会匹配日语字符“あ”,因为它不是控制字符,或者[:print:]始终是ASCII代码0x20到0x7E?
是否有任何字符类,包括Perl REs,可用于匹配除控制字符之外的任何内容?如果[:print:]仅包含ASCII范围内的字符,我会假设[:cntrl:]也是如此。
5个回答

6
echo あ| perl -nle 'BEGIN{binmode STDIN,":utf8"} print"[$_]"; print /[[:print:]]/ ? "YES" : "NO"'

这个方法大部分情况下都有效,虽然会生成一个宽字符的警告。但是它给你了一个思路:你必须确保处理的是真正的Unicode字符串(检查utf8::is_utf8)。或者直接查看perlunicode - 整个主题仍然让我头晕。


1
你可以通过在命令行上提供选项-CS来摆脱丑陋的BEGIN{binmode STDIN, ":utf8"} kludge。 - moritz
这也将消除警告,因为它将设置STDOUT与STDIN相同。 - moritz
如果 OP 正在编写一个处理此问题的模块而不是独立脚本,那么这可能不是一个很好的选择。因此,我会留下我的解决方案和你的修复,希望 OP 能够弄清楚哪个更适合他/她的情况。谢谢 :-) - Tanktalus
这个模式是错误的。[[:print:]]将匹配“\x{3099}”,这不是一个完全组合的字符!请查看我的答案以获取有效的模式。 - daxim

5
我认为你不需要使用本地化,而是需要使用Unicode。如果你已经解码了文本字符串,\w将匹配任何语言中的单词字符,\d不仅匹配0..9,还匹配每个Unicode数字等。在正则表达式中,你可以使用\p{PropertyName}查询Unicode属性。对你来说特别有趣的可能是\p{Print}。这里是所有可用Unicode字符属性的列表
我写了一篇关于Unicode和Perl基础和微妙之处的文章,它应该能让你很好地理解如何让perl将你的字符串识别为字符序列,而不仅仅是字节序列。
更新:使用Unicode时,你不会得到与语言相关的行为,而是无论语言都会得到合理的默认值。这可能是你想要的,也可能不是,但对于可打印/控制字符的区分,我不认为你需要与语言相关的行为。

4

\X 匹配一个完全组成的字符(序列)。证明如下:

#!/usr/bin/env perl
use 5.010;
use utf8;
use Encode qw(encode_utf8);

for my $string (qw(あ ご ご), "\x{3099}") {
    say encode_utf8 sprintf "%s $string", $string =~ /\A \X \z/msx ? 'ok' : 'nok';
}

测试数据包括:普通字符、预组合字符、组合字符序列和一个组合字符(它本身“不计入”,是Unicode第3章的简化版)。
\X替换为[[:print:]],可以看到Tanktalus的答案对于最后两种情况产生了错误匹配。

2

是的,这些表达式依赖于语言环境。


你能否提供一个环境和/或正则表达式实现,使得[:print:]能够遵循日语UTF-8区域设置/编码?我在Linux中使用Perl和日语UTF-8区域设置/编码,但它无法匹配日语字符。 - dreamlax

1

您可以始终使用字符类[^[:cntrl:]]来匹配非控制字符。


1
这与Unicode的控制字符不匹配(在我的环境设置和使用Perl时)。有用于更改文本方向等的Unicode控制字符。使用[^[:ctrnl:]]将匹配这些Unicode字符,但不匹配ASCII字符。 - dreamlax

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