Nat*_*enn 5 regex string perl character-encoding
我正在编写的库中的一个函数返回一个字符串,当尝试通过正则表达式或index函数定位 unicode 字符时,该字符串会出现问题。字符串正常打印(使用 Sublime text 的控制台进行 unicode 打印),如下所示:
<xml>V???</xml>
Run Code Online (Sandbox Code Playgroud)
而且我想这样的搭配吧:$string =~ m/V???/。我正在使用utf8.
我很抱歉我无法重现一个最小的破坏示例,因为当我自己构造字符串并尝试匹配它时,一切正常。我尝试使用此站点中的hexdump函数,但它为库返回的字符串和我构造的字符串 ( ): 中的 unicode 字符打印相同的十六进制序列。库中的那个关闭了 utf 标志,构建的那个没有,但另一项测试表明这不是问题。$string2 = 'V???'56 e6 97 a5 ed 95 9c e1 ba bf
关于问题的根源,我只有一个线索:带有use re 'debug';. 它给出了以下消息:
Matching REx "V%x{65e5}%x{d55c}%x{1ebf}" against "%n<xml>V%x{e6}%x{97}%x{a5}%x{ed}%x{95}%x{9c}%x{e1}%x{ba}"...
Run Code Online (Sandbox Code Playgroud)
它正在打印字符“?” 在正则表达式中作为%x{65e5}和有问题的字符串中相同的字符作为%x{e6}%x{97}. 其他 unicode 字符的打印方式类似。
任何有调试字符串和编码经验的人都可以告诉我为什么 regex 并且index找不到我知道的字符串中存在的 unicode 字符,以及如何让这些函数找到它们?
让我们制作一个可重现的测试用例:
\n\n生成输入文件:
\n\n$ perl -E\'say "<xml>V\\xe6\\x97\\xa5\\xed\\x95\\x9c\\xe1\\xba\\xbf</xml>"\' >test.xml\n$ cat test.xml\n<xml>V\xe6\x97\xa5\xed\x95\x9c\xe1\xba\xbf</xml>\nRun Code Online (Sandbox Code Playgroud)\n\n这会将一些字节写入文件。请注意,我的终端模拟器使用 UTF-8。
尝试天真地匹配输入:
\n\n$ cat test.pl\nuse strict; use warnings; use utf8; use autodie; use feature \'say\';\nopen my $fh, "<", shift @ARGV;\n\nmy $s = <$fh>;\nsay "$s ", $s =~ m/V\xe6\x97\xa5\xed\x95\x9c\xe1\xba\xbf/ ? "matches" : "doesn\'t match";\nsay "string = ", map { sprintf "\\\\x{%x}", ord } split //, $s;\n$ perl test.pl test.xml\n<xml>V\xe6\x97\xa5\xed\x95\x9c\xe1\xba\xbf</xml>\n doesn\'t match\nstring = \\x{3c}\\x{78}\\x{6d}\\x{6c}\\x{3e}\\x{56}\\x{e6}\\x{97}\\x{a5}\\x{ed}\\x{95}\\x{9c}\\x{e1}\\x{ba}\\x{bf}\\x{3c}\\x{2f}\\x{78}\\x{6d}\\x{6c}\\x{3e}\\x{a}\nRun Code Online (Sandbox Code Playgroud)\n\n哦,所以文件中的字符串被视为字节字符串,而不是正确解码的代码点。谁能想到呢?
让我们添加:utf8PerlIO 层:
$ cat test-utf8.pl\nuse strict; use warnings; use utf8; use autodie; use feature \'say\';\nopen my $fh, "<:utf8", shift @ARGV;\n\nmy $s = <$fh>;\nsay "$s ", $s =~ m/V\xe6\x97\xa5\xed\x95\x9c\xe1\xba\xbf/ ? "matches" : "doesn\'t match";\nsay "string = ", map { sprintf "\\\\x{%x}", ord } split //, $s;\n$ perl test-utf8.pl test.xml\nWide character in say at test-utf8.pl line 5, <$_[...]> line 1.\n<xml>V\xe6\x97\xa5\xed\x95\x9c\xe1\xba\xbf</xml>\n matches\nstring = \\x{3c}\\x{78}\\x{6d}\\x{6c}\\x{3e}\\x{56}\\x{65e5}\\x{d55c}\\x{1ebf}\\x{3c}\\x{2f}\\x{78}\\x{6d}\\x{6c}\\x{3e}\\x{a}\nRun Code Online (Sandbox Code Playgroud)\n\n现在它匹配了,因为我们已经从文件中读取了正确解码的代码点。
你得到相同的输出吗?如果您没有获得类似的输出,那么您使用的是什么 perl/OS 组合(这是 Ubuntu GNU/Linux 上的 perl 5.18.1)。
\n\n这段代码还存在一些问题: 有多种方法来表示\xe1\xba\xbf. 因此,您应该规范化正则表达式和输入中的字符串:
use Unicode::Normalize \'NFC\';\nmy $regex_body = NFC "V\xe6\x97\xa5\xed\x95\x9c\xe1\xba\xbf";\nmy $s = NFC scalar <$fh>;\n\n... m/\\Q$regex_body/ ...\nRun Code Online (Sandbox Code Playgroud)\n