utf-8的perl binmode仅用于\ x {codepoint},而不是用于三字节编码的\ x编码

Dav*_*lar 0 unicode perl encoding utf-8

欧洲字符是UTF-8 0xe282ac

我正在尝试在perl中使用字符串,并将UTF-8字符输出到STDOUT.

所以我用'use utf8'将脚本设置为UTF-8

我将STDOUT设置为带有'binmode'的 UTF-8 .

示例脚本是:

use utf8;
binmode STDOUT, ':utf8';
print "I owe you 160\x{20ac}\n";
print "I owe you 80\xe2\x82\xac\n";  # UTF-8 encoding?
Run Code Online (Sandbox Code Playgroud)

\ x {codepoint}工作正常,但编码UTF-8会给我一个错误:

I owe you 160€
I owe you 80â¬
Run Code Online (Sandbox Code Playgroud)

Gra*_*ean 5

如果你想要一个由三个字节组成的字符串E2 82 AC,你可以像这样声明它:

my $bytes = "\xE2\x82\xAC";
Run Code Online (Sandbox Code Playgroud)

\xXX双引号字符串中的表单使用两个十六进制数字(总是两个)来表示一个字节.

上面的字符串包含3个字节.如果我们将字符串传递给length函数,它将返回3:

say 'Length of $bytes is: ' . length($bytes);    # 3
Run Code Online (Sandbox Code Playgroud)

Perl无法知道这三个字节是否用于表示欧元符号.它们同样可以是JPEG文件内部的三字节序列,也可以是ZIP文件,也可以是遍历网络的SSL编码的TCP数据流.Perl不知道或不关心 - 它只是三个字节.

如果你真的想要一个字符串(而不是字节),那么你需要以允许Perl使用其Unicode字符的内部表示将它们存储在内存中的方式提供字符数据.一种方法是在源代码中以UTF8格式提供非ASCII字符.如果你这样做,你需要use utf8在脚本的顶部说出告诉Perl解释器将非ASCII字符串文字视为utf8:

use utf8;

my $euro_1 = "€";
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用带有1-5个十六进制字符的表单\ x {X ...}来表示Unicode代码点编号.这将声明一个相同的字符串:

my $euro_2 = "\x{20ac}";
Run Code Online (Sandbox Code Playgroud)

这些字符串中的每一个都包含Perl内部编码中的欧元字符的多字节表示.Perl知道字符串是字符串,因此length函数将在每种情况下返回1(对于1个字符):

say 'Length of $euro_1 is: ' . length($euro_1);    # 1
say 'Length of $euro_2 is: ' . length($euro_2);    # 1
Run Code Online (Sandbox Code Playgroud)

Perl内部表示字符串的定义特征是它 Perl中使用.如果要将数据写入文件或套接字,则需要将字符串编码为字节序列:

use Encode qw(encode);

say encode('UTF-8', $euro_1);
Run Code Online (Sandbox Code Playgroud)

也可以使用binmode或参数来open表示写入特定文件句柄的任何字符串都应编码为特定的编码.

binmode(STDOUT, ':encoding(utf-8)');

say $euro_1;
Run Code Online (Sandbox Code Playgroud)

这只适用于字符串.如果我们使用原始的3字节字符串$bytes并使用其中一个encode或IO层,我们最终会得到垃圾,因为Perl将获取每个字节并将其转换为UTF8.因此\xE2输出为\xC3\xA2,\x82将输出为\xC2\x82,依此类推.

但是,我们可以使用该Encode::Decode函数将3字节$ bytes字符串转换为Perl内部字符表示形式的单个字符串:

use Encode qw(decode);

my $bytes = "\xE2\x82\xAC";
my $euro_3 = decode($bytes);

say 'Length of $euro_3 is ' . length($euro_3);    # 1
Run Code Online (Sandbox Code Playgroud)

一个小的挑剔:在你原来的问题中,你说的20AC是欧元符号 UTF-16表示.事实上,有两种不同的UTF-16表示形式:UTF16BE和UTF16LE,后者使用相反的顺序:AC20.