在Perl中将UTF8字符串转换为ASCII

16

我尝试了谷歌和StackOverflow推荐的所有方法(我能找到的),包括使用Encode。我的代码已经可以工作,但它只使用UTF8并且会出现宽字符警告。我知道如何解决这些警告,但我没有在其他地方使用UTF8,因此我想转换它,而不必修改我的代码以适应它。以下是我的代码:

my $xml = XMLin($content);
# Populate the @titles array with each item title.
my @titles;
for my $item (@{$xml->{channel}->{item}}) {
    my $title = Encode::decode_utf8($item->{title});
    #my $title = $item->{title};
    #utf8::downgrade($title, 1);
    Encode::from_to($title, 'utf8', 'iso-8859-1');
    push @titles, $title;
}
return @titles;

注释掉的部分是我尝试过的其他一些方法。我很清楚我在这里并不知道我在做什么。但我只想最终得到一个普通的ASCII字符串。任何想法都将不胜感激。谢谢。

3个回答

20
答案取决于您想如何使用标题。有三种基本的方式:
- 代表 UTF-8 编码字符串的字节。 - Unicode 字符串。 - 代表原始字节的 UTF-8 编码字符串。
应该使用第一种格式来存储 UTF-8 编码字符串,无论是在磁盘上还是通过网络发送或超出程序范围的任何内容。
字符的概念是 Perl 内部的。当您执行 Encode::decode_utf8 后,一堆字节将尝试转换为 Perl 可以识别的字符串。Perl 虚拟机(以及编写 Perl 代码的程序员)无法通过解码输入的 UTF-8 字节并将其编码为输出的 UTF-8 字节来外部化该概念。例如,您的程序作为输入接收两个字节,可以知道它们表示 UTF-8 编码的字符,假设这些字节是 `0xC3 0xB6`。在这种情况下,decode_utf8 返回一个表示一个字符的表示形式,而不是两个字节:ö。
然后,您可以在Perl中继续操作该字符串。为了更好地说明差异,请考虑以下代码:
my $bytes = "\xC3\xB6";
say length($bytes); # prints "2"
my $string = decode_utf8($bytes);
say length($string); # prints "1"
  • ASCII的特殊情况,是UTF-8的子集。

    ASCII是Unicode的一个非常小的子集,在该范围内的字符由单个字节表示。将Unicode转换为ASCII是一种固有的有损操作,因为大多数Unicode字符不是ASCII字符。当尝试将Unicode字符串强制转换为ASCII时,您要么被迫删除字符串中不在ASCII中的每个字符,要么尝试将Unicode字符映射到它们最接近的ASCII等效字符(在绝大多数情况下是不可能的)。

如果您收到宽字符警告,则意味着您正在尝试操作(可能输出)无法表示为ASCII或ISO-8859-1的Unicode字符。

如果您不需要将XML文档中的标题作为字符串进行操作(也许是输出),我建议您将其保留为UTF-8字节(我要提醒您在字符串中不要混合使用字节和字符)。如果您需要操作它,则解码,操作后再将其编码为UTF-8进行输出。

如需进一步了解,请使用perldoc查看perlunitutperlunifaqperlunicodeperluniintroEncode


1
所以基本上就像我猜想的那样。我对这个问题的理解完全错误了。好吧,谢谢你花时间澄清。出于某种原因,我以为我能够将我的UTF8字符串强制转换成一些ASCII形式,但听起来最多只是一个混乱的hack。我想我只能勇敢地面对UTF8编码了。 - Mark C
3
如果您必须将UTF-8转换为ASCII,则需要使用[Text :: Unidecode](http://search.cpan.org/perldoc?Text::Unidecode)。 - cjm
@cjm 正是我需要的。这个程序将 utf8 字符转换为最接近的 ASCII 可视化替代字符。非常感谢! - oᴉɹǝɥɔ

7
尽管这是一个老问题,但我花了几个小时(!)尝试做更多或更少相同的事情!也就是:从UTF-8 XML文件中读取数据,并将该数据转换为Windows-1252代码页(我也可以使用Latin1,ISO-8859-1等),以便能够创建带有重音字母的文件名。
经过许多实验甚至更多的搜索,我终于成功地进行了转换。 "诀窍"是使用Encode::encode而不是Encode::decode。
例如,给定原始问题中的代码,从UTF-8转换的正确(或至少是一种)方法如下:
my $title = Encode::encode("Windows-1252", $item->{title});

或者

my $title = Encode::encode("ISO-8859-1", $item->{title});

或者

my $title = Encode::encode("<your-favourite-codepage-here>", $item->{title});

我希望这篇文章能够帮助到其他遇到类似问题的人!

2

您可以使用以下代码来消除警告。假设您想使用UTF8编码,这通常不应该是一个问题。

binmode(STDOUT, ":encoding(utf8)");


1
你不需要使用冒号,但是除非你使用了Perl 5.10.1或更高版本的use autodie编译指示,否则最好检查返回值以确保没有拼写错误。还有一个名为PERL_UNICODE的环境变量,可以将其设置为S。您可以使用‑C0命令行标志在运行时覆盖它。更常见的情况是您想要添加到它中,例如使用‑CSAD。但是请小心,因为现在所有未标记的流默认为UTF-8,这通常会导致问题。因此,它不是一个好的默认选项。 - tchrist

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