为什么在Perl正则表达式中"\w"无法匹配Unicode单词字符(例如“ğ, İ, ş, ç, ö, ü”)?

5
为什么Perl正则表达式中的"\w"不能匹配Unicode单词字符(例如,"ğ,İ,ş,ç,ö,ü")?
我尝试在正则表达式m{\w+}g中包含这些字符。但是,它无法匹配"ğ,İ,ş,ç,ö,ü"。
如何解决这个问题?
use strict;
use warnings;
use v5.12;
use utf8;

open(MYINPUTFILE, "< $ARGV[0]");

my @strings;
my $delimiter;
my $extensions;
my $id;

while(<MYINPUTFILE>)
{
    my($line) = $_;
    chomp($line);
    print $line."\n";
    unshift(@strings,$line =~ /\w+/g);
    $delimiter = /[._\s]/;
    $extensions = /pdf$|doc$|docx$/;
    $id = /^200|^201/;
}

foreach(@strings){
    print $_."\n";
}

输入文件格式如下:

Çidem_Şener
Hüsnü Tağlip
...

输出文件格式如下:

H�

sn�

Ta�

lip

�

idem_�

ener

在代码中,我试图读取文件并将数组中的每个字符串取出。(分隔符可以是_.\s)。

相关但不是重复的:https://dev59.com/l2035IYBdhLWcg3wNdPg,http://stackoverflow.com/questions/1796573/regex-word-breaker-in-unicode - user166390
你需要像这样说:open MYINPUTFILE, '<:encoding(UTF-8)', $ARGV[0] ...。否则,你的输入是原始的(八位字节序列),而不是按照你的期望进行解释的。 - mob
未识别的字符 \xC3; 在 C:/Users/erogol/Documents/Aptana Studio 3 Workspace/Automata/file.txt 第1行,Muft<-- HERE 附近第5列标记为 <-- HERE。然后它会给出此错误,并且无法识别 "ğü..." 字符。 - erogol
你的错误在于你正在以二进制模式而不是UTF-8文本读取文件。将以下代码添加到你的程序中:use open qw(:std :utf8); use warnings qw(FATAL utf8);,一切应该会好得多。请参见 The Perl Unicode Cookbook - tchrist
3个回答

3
请确保Perl将数据视为UTF-8格式。 例如,如果它被嵌入到脚本中:
#!/usr/bin/perl

use strict;
use warnings; 
use v5.12;
use utf8;   # States that the Perl program itself is saved using utf8 encoding

say "matched" if "ğİşçöü" =~ /^\w+$/;

这将输出匹配。如果我删除use utf8;这一行,则不会输出。


但是我的不是“ğİşçöü”准确的。它们可能与所有其他字符一起出现在用户名中。 - erogol
2
我在答案中举例使用了这个。关键是,如果字符串的内部“is UTF8”标志处于打开状态,\w将匹配您想要的字符。 - Quentin

3

\w 可以匹配 ğ İ ş ç ö ü 中的任何一个。

'ğİşçöü' =~ /\A \w+ \z/msx;     # true

您可能犯了错误,忘记将输入从八位字节解码为Perl字符。我怀疑您的正则表达式检查的是字节级别的内容,而不是像预期的那样在字符级别上进行检查。
阅读http://p3rl.org/UNIhttp://training.perl.com/scripts/perlunicook.html以了解Perl中编码的相关主题。

编辑:

问题很可能出现在这里(如果没有文件内容,我无法确定):

open(MYINPUTFILE, "< $ARGV[0]");

查找文件的编码方式,可能是 UTF-8Windows-1254。进行重写,例如:

open $in, '<:utf8', $ARGV[0];
open $in, '<:encoding(Windows-1254)', $ARGV[0];

同样地,向标准输出(程序末尾附近)打印字符也存在编码缺失的问题,因此这种方式也是有问题的。℞ 16: 声明 STD{IN,OUT,ERR} 为本地编码 展示了一种正确的做法。

1

Unicode可能会带来一些挑战,而Perl也有其自己的特点。基本上,Perl在与Unicode相关的所有输入/输出途径周围设置了一个防火墙。您必须告诉Perl路径是否具有编码。如果是这样,规则是对于任何输入进行解码和/或对于任何输出进行编码。

解码将数据从{编码}转换为Perl使用的内部表示形式,这可能是字节和代码点的组合。

编码则完全相反。

因此,实际上可以将“解码”和“编码”分别用于两种不同的编码。您只需要告诉它是什么。编码/解码通常通过文件I/O层完成,但您可以使用Encode模块(分发的一部分)手动在编码之间进行转换。

关于Unicode的perldocs并不是轻松的阅读材料。

这里有一个示例,可能有助于可视化它(还有许多其他方法)。

use strict;
use warnings;
use Encode;


# This is an internalized string with these UTF-8 codepoints
# ----------------------------------------------
my $internal_string_1 = "\x{C7}\x{69}\x{64}\x{65}\x{6D}\x{5F}\x{15E}\x{65}\x{6E}\x{65}\x{72}\x{20}\x{48}\x{FC}\x{73}\x{6E}\x{FC}\x{20}\x{54}\x{61}\x{11F}\x{6C}\x{69}\x{70}";


# Open a temp file for writing as UTF-8.
# Output to this file will be automatically encoded from Perl internal to UTF-8 octets.
# Write the internal string.
# Check the file with a UTF-8 editor.
# ----------------------------------------------
open (my $out, '>:utf8', 'temp.txt') or die "can't open temp.txt for writing $!";
print $out $internal_string_1;
close $out;


# Open the temp file for readin as UTF-8.
# All input from this file will be automatically decoded as UTF-8 octets to Perl internal.
# Read/decode to a different internal string.
# ----------------------------------------------
open (my $in, '<:utf8', 'temp.txt') or die "can't open temp.txt for reading $!";
$/ = undef;
my $internal_string_2 = <$in>;
close $in;


# Change the binmode of STDOUT to UTF-8.
# Output to STDOUT will now be automatically encoded from Perl internal to UTF-8 octets.
# Capture STDOUT to a file then check with a UTF-8 editor.
# ----------------------------------------------
binmode STDOUT, ':utf8';
print $internal_string_2, "\n\n";


# Use encode() to convert an internal string to UTF-8 octets
# Format the UTF-8 octets to hex values
# Print to STDOUT
# ----------------------------------------------
my $octets = encode ("utf8", $internal_string_2);
print "Encoded (out) string -> UTF-8 (octets):\n";
print "   length  =  ".length($octets)."\n";
print "   octets  =  $octets\n";
print "   HEX val =  ";
for (split //, $octets) {
    printf ("0x%X ", ord($_));
}
print "\n\n";


# Use decode() to convert external UTF-8 octets to an internal string.
# Format the internal string to codepoints (hex values).
# Print to STDOUT.
# ----------------------------------------------
my $internal_string_3 = decode ("utf8", $octets);
print "Decoded (in) string <- UTF-8 (octets):\n";
print "   length      =  ".length($internal_string_3)."\n";
print "   string      =  $internal_string_3\n";
print "   code points =  ";
for (split //, $internal_string_3) {
    printf ("\\x{%X} ", ord($_));
}

输出

Çidem_Şener Hüsnü Tağlip

Encoded (out) string -> UTF-8 (octets):
   length  =  29
   octets  =  Ãidem_Åener Hüsnü TaÄlip
   HEX val =  0xC3 0x87 0x69 0x64 0x65 0x6D 0x5F 0xC5 0x9E 0x65 0x6E 0x65 0x72 0x20 0x48 0xC3 0xBC 0x73 0x6E 0xC3 0xBC 0x20 0x54 0x61 0xC4 0x9F 0x6C 0x69 0x70

Decoded (in) string <- UTF-8 (octets):
   length      =  24
   string      =  Çidem_Şener Hüsnü Tağlip
   code points =  \x{C7} \x{69} \x{64} \x{65} \x{6D} \x{5F} \x{15E} \x{65} \x{6E} \x{65} \x{72} \x{20} \x{48} \x{FC} \x{73} \x{6E} \x{FC} \x{20} \x{54} \x{61} \x{11F} \x{6C} \x{69} \x{70}

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