为什么对Unicode字符的Perl字符串操作会给字符串添加垃圾?

Mik*_*ike 4 regex unicode perl internationalization

Perl的:

$string =~ s/[áàâã]/a/gi; #This line always prepends an "a"
$string =~ s/[éèêë]/e/gi;
$string =~ s/[úùûü]/u/gi;
Run Code Online (Sandbox Code Playgroud)

这个正则表达式应该将"été"转换为"ete".相反,它将它转换为"aetae".换句话说,它为每个匹配元素添加"a".甚至"à"也会转换为"aa".

如果我将第一行更改为此

$string =~ s/(á|à|â|ã)/a/gi;
Run Code Online (Sandbox Code Playgroud)

它有效,但是......现在它预先设置了e每个匹配的元素(比如"eetee").

即使我找到了合适的解决方案,为什么它会这样做?

编辑1:

我添加了"use utf8;",但它没有改变行为(尽管它在JavaScript/AJAX中打破了我的输出).

EDIT2:

Stream源自jQuery执行的Ajax请求.它起源的网站设置为UTF-8.

我正在使用Perl v5.10(perl -v返回"这是perl,为i586-linux-thread-multi构建的v5.10.0").

小智 8

问题很可能是没有

use utf8;
Run Code Online (Sandbox Code Playgroud)

(或其对应于您正在使用的任何编码系统)在您的程序中.你在那里的奇怪的替换看起来像字节而不是字符正则表达式替换的问题.

#!/usr/local/bin/perl
use warnings;
use strict;
use utf8;
binmode STDOUT, "utf8";
my $string = "été";

$string =~ s/[áàâã]/a/gi; #This line always prepends an "a"
$string =~ s/[éèêë]/e/gi;
$string =~ s/[úùûü]/u/gi;

print "$string\n";
Run Code Online (Sandbox Code Playgroud)

版画

ete
Run Code Online (Sandbox Code Playgroud)

如果要从文件或标准输入读取输入,请确保将流设置为utf8或适合编码的任何内容.供STDIN使用

binmode STDOUT, "utf8";
Run Code Online (Sandbox Code Playgroud)

如果您正在阅读文件,请使用

open my $file, "<:utf8", "file_name"
Run Code Online (Sandbox Code Playgroud)

获得正确的编码.如果它不是UTF-8,请使用encoding(name)而不是utf8.

  • 鉴于Mike已经'使用utf8;' 在他的源代码中,Unicode源代码将被接受就好了.这表明他的输入字符串没有被正确解释.请记住,utf8编译指示会影响程序代码而不是源代码. (2认同)

oyl*_*gul 7

但你真的想要使用正则表达式吗?也许类似Text :: Unidecode的东西会更好

$ perl -Mutf8 -MText::Unidecode -E 'say unidecode("été")'
ete
Run Code Online (Sandbox Code Playgroud)


Mez*_*Mez 5

这可能是因为您正在使用UTF8字符串,并且它正在解析它们,就好像它们不是或类似的一样.

而不是使用像[áàâã]你这样的东西应该probasaly使用类似的东西 [\xE1-\xE5]

并且可能use utf8;也在你的代码中使用


Ian*_*and 5

我怀疑发生的事情是你的正则表达式的[áàâã]部分实际上并不匹配字符,而是匹配字节.这些字符的UTF-8编码在正则表达式中看起来像这样:

[\xC3\xA1\xC3\xA0\xC3\xA2\xC3\xA3]
Run Code Online (Sandbox Code Playgroud)

因此,当正则表达式被输入时,例如'é'(\ xC3\xA9),它一次查看一个字节,匹配\ xC3,并用'a'替换它.它为它可以找到的所有\ xC3字节执行此操作.所以,'été'变成了'a\xA9ta\xA9'.

然后是第二个正则表达式,如下所示:

[\xc3\xA9\xC3\xA8\xC3\xAA\xC3\xAB]
Run Code Online (Sandbox Code Playgroud)

来了,它匹配\ xA9部分,并用'e'替换它.所以现在,'a\xA9ta\xA9'变成'aetae'.

当你用[á|à|ã|]替换[áàâã]时,它会在第一遍中正确匹配完整的字符,但是你的第二个正则表达式有原始问题,\ xC3字符被'e'替换代替.

如果仍然发生这种情况,即使使用use utf8;,那么Perl正则表达式引擎中可能存在错误(或至少存在限制).