Perl字符串内部

Perl字符串内部,perl,string,encoding,Perl,String,Encoding,如何在内部表示perl字符串?使用什么编码?如何正确处理不同的编码 我已经使用perl很长时间了,但它没有在不同的编码中包含很多字符串处理,当我遇到与编码有关的小问题时,我通常会求助于一些萨满动作 在此之前,我一直认为perl字符串是字节序列,非常适合我的任务。现在我需要对UTF-8编码文件进行一些处理,这里开始出现问题 首先,我将文件读入如下字符串: open(my $in, '<', $ARGV[0]) or die "cannot open file $ARGV[0] for rea

如何在内部表示perl字符串?使用什么编码?如何正确处理不同的编码

我已经使用perl很长时间了,但它没有在不同的编码中包含很多字符串处理,当我遇到与编码有关的小问题时,我通常会求助于一些萨满动作

在此之前,我一直认为perl字符串是字节序列,非常适合我的任务。现在我需要对UTF-8编码文件进行一些处理,这里开始出现问题

首先,我将文件读入如下字符串:

open(my $in, '<', $ARGV[0]) or die "cannot open file $ARGV[0] for reading";
binmode($in, ':utf8');

my $contents;

{
    local $/;
    $contents = <$in>;
}

close($in);
我得到两样东西:一个警告
宽字符在第
行打印,另一个垃圾在控制台中。因此,我可以得出结论,perl字符串有一个“字符”的概念,它可以是“宽”也可以不是“宽”,但在打印时,这些“宽”字符在控制台中表示为多个字节,而不是单个“字符”。 (我现在想知道为什么我以前使用二进制文件的所有经验都能像我预期的那样在没有任何“字符”问题的情况下工作)

为什么我会在控制台中看到垃圾?如果perl在某些已知编码中将字符串存储为字符,我认为找出控制台编码并正确打印文本不会有大问题。(顺便说一句,我使用Windows)

如果perl将字符串存储为可变宽度字符序列(例如,使用相同的UTF-8编码),为什么要这样做?从我的C语言经验来看,处理字符串是一件痛苦的事情

更新

我使用两台计算机进行测试,一台运行安装了英语语言包的Windows7x64,但使用ActivePerl5.10.1x64的俄语区域设置(因此我将cp866作为OEM代码页,将cp1251作为ANSI);另一个使用Cygwin Perl 5.10.0运行Windows XP 32位俄语本地化


多亏了这些链接,现在我对正在发生的事情以及应该如何做有了更深入的了解。

您应该提到您的实际Windows和Perl版本,因为这实际上取决于您使用的版本和安装的语言包。
否则,请先查看手册-

Perl使用逻辑宽度的字符在内部表示字符串

这将证实你的陈述


Windows没有完全安装所有UTF8字符-因此这可能是您出现问题的原因。您可能需要安装额外的语言包。

在读取文件之前设置utf8很好,它会自动将字节解码为内部编码。(这也是UTF-8,但您不需要知道,也不应该依赖它。)

在打印之前,需要将字符编码回字节

use Encode;  
utf8::encode($contents);
对于unicode以外的其他编码,还有一种双参数编码形式。(那句话太重复了,不是吗?)

这里有一个很好的参考。(可能会更多,但这是我的第一篇帖子。)也可以查看perlunitut,以及关于Joel on Software的unicode文章


哦,它必须使用多字节字符串,因为否则它就不是unicode。

Perl字符串在内部存储在两种编码中的一种,要么是面向字节的8位本机编码,要么是UTF-8。对于向后可比性,假设所有I/O和字符串都采用本机编码,除非另有规定。本机编码通常为8位ASCII,但这可以通过
use locale
更改

在示例中,您在输入句柄上调用binmode,将其更改为使用
:utf8
语义。这样做的一个效果是,从这个句柄读取的所有字符串都将被编码为UTF-8<默认情况下,code>print写入
STDOUT
,而
STDOUT
默认为预期的本机编码字符

如果Perl试图做正确的事情,它将允许将UTF-8字符串发送到本机编码的输出,但是如果该句柄没有附加编码,那么它必须猜测如何输出多字节字符,并且几乎肯定猜错了。这就是警告的意思,多字节字符被发送到只需要单字节字符的流,结果可能是该字符在翻译过程中被损坏


根据您想要完成的任务,您可以使用dylan提到的编码模块将UTF-8数据转换为可以安全打印的单字节字符集,或者如果您知道连接到
STDOUT
的任何内容都可以处理UTF-8,您可以使用
binmode(STDOUT,,:utf8')
告诉Perl您希望发送到STDOUT的任何数据都以UTF-8的形式发送。

通过多字节字符串,我的意思是可变宽度编码。无论如何,我不明白为什么我必须显式地进行转换:我指定了输入数据编码为什么我必须采取一些额外的步骤?您已经指定了输入编码。你做你的事。然后指定输出编码。我认为我提到的文章解释得更好。不要使用
utf8
包中的函数。文档说:除了告诉Perl您的脚本是用UTF-8编写的之外,不要将这个pragma用于任何其他用途。相反,请始终使用
Encode
模块。倒数第二个句子毫无意义。您似乎提到了字体,但这与编码无关。如果defualt编码是8位ASCII(或任何其他8位编码),为什么Perl将UTF-8字符串打印为原始字节(即,为打印字符串中的每个西里尔字符向控制台打印两个字符)UTF-8字符串从perl的角度看不是字节,而是字符。此IIRC的一个奇怪结果是,当打印到未定义编码的句柄时,它会将大于255的Unicode代码点截断为较低的8位。
use Encode;  
utf8::encode($contents);