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

时间:2009-10-15 12:39:02

标签: regex perl unicode internationalization

的Perl:

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

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

如果我将第一行更改为此

$string =~ s/(á|à|â|ã)/a/gi;

它有效,但是......现在它为每个匹配的元素(如“eetee”)添加e

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

编辑1:

我添加了“use utf8;”,但它没有改变行为(虽然它在JavaScript / AJAX中打破了我的输出。)

EDIT2:

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

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

7 个答案:

答案 0 :(得分:8)

问题很可能是没有

use utf8;

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

#!/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";

打印

ete

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

binmode STDOUT, "utf8";

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

open my $file, "<:utf8", "file_name"

获得正确的编码。如果不是UTF-8,请使用encoding(name)代替utf8

答案 1 :(得分:7)

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

$ perl -Mutf8 -MText::Unidecode -E 'say unidecode("été")'
ete

答案 2 :(得分:5)

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

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

也可能在您的代码中使用use utf8;

答案 3 :(得分:5)

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

[\xC3\xA1\xC3\xA0\xC3\xA2\xC3\xA3]

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

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

[\xc3\xA9\xC3\xA8\xC3\xAA\xC3\xAB]

出现,它匹配\ xA9部分,并用'e'替换它。现在,'a \ xA9ta \ xA9'变成'aetae'。

当你用[á|à|â|ã]替换[áàâã]时,那么在第一遍中正确匹配完整的字符,但是你的第二个正则表达式有原始问题,并且\ xC3字符被替换为'e'而不是。

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

答案 4 :(得分:2)

有些东西告诉我这是因为它不知道如何用带重音的字符来表现。通过查看你的正则表达式,一切似乎都很好。您可能想要添加:

use utf8;

答案 5 :(得分:2)

这也可能是Unicode Normalisation的问题,因为某些系统(我正在看你,OS X)将扩展的Latin1字形表示为特定的规范化表示,当你引用一个字符时它可以破坏正则表达式特别是不使用unicode或hex表示。

答案 6 :(得分:1)

我会说你不应该在这里使用正则表达式。实现这一目标的最简单方法(虽然这可能是不可取的)是将输入字符串转换为US ASCII。相应的转换表应该知道e最接近é

另一种选择是使用Unicode并将您的字符串规范化为NFD。这会将所有重音字母分解成基本字母+变音符号。然后你可以通过你的字符串并删除所有组合的变音字符。