令我惊恐的是,我刚刚发现
chr
不适用于 Unicode,尽管它确实有 的功能。 手册页几乎一目了然
返回字符集中该 NUMBER 表示的字符。例如,chr(65)”在 ASCII 或 Unicode 中都是“A”,而 chr(0x263a) 是 Unicode 笑脸。
事实上我可以使用
打印笑脸perl -e 'print chr(0x263a)'
但是像
chr(0x00C0)
之类的东西不起作用。我看到我的 perl v5.10.1 有点古老,但是当我在源代码中粘贴各种奇怪的字母时,一切都很好。
我尝试过诸如
use utf8
和use encoding 'utf8'
之类的有趣的事情,我还没有尝试过像use v5.12
和use feature 'unicode_strings'
这样有趣的事情,因为它们不适用于我的版本,我在玩弄Encode::decode
发现我不需要解码,因为我没有要解码的字节数组。我阅读了比以前更多的文档,并发现了很多有趣的东西,但没有任何帮助。它看起来像是一种 Unicode Bug,但没有给出可用的解决方案。而且我不关心整个字符串语义,我需要的只是一个简单的函数。
那么如何将数字转换为由与其对应的单个字符组成的字符串,以便例如
real_chr(0xC0) eq 'À'
成立?
我得到的第一个答案解释了有关 IO 的所有内容,但我仍然不明白为什么
#!/usr/bin/perl -w
use strict;
use utf8;
use encoding 'utf8';
print chr(0x00C0) eq 'À' ? 'eq1' : 'ne1', " - ", chr(0x263a) eq '☺' ? 'eq1' : 'ne1', "\n";
print 'À' =~ /\w/ ? "match1" : "no_match1", " - ", chr(0x00C0) =~ /\w/ ? "match2" : "no_match2", "\n";
打印
ne1 - eq1
match1 - no_match2
表示手动输入的
'À'
与chr(0x00C0)
不同。而且,前者是单词构成字符(正确!),而后者不是(但应该是!)。
首先,
perl -le'print chr(0x263A);'
有越野车。 Perl 甚至告诉你这么多:
Wide character in print at -e line 1.
这不符合“工作”的条件。因此,虽然它们在如何无法提供您想要的方面有所不同,但以下两者都没有给您您想要的:
perl -le'print chr(0x263A);'
perl -le'print chr(0x00C0);'
要正确输出这些 Unicode 代码点的 UTF-8 编码,您需要告诉 Perl 使用 UTF-8 编码 Unicode 点。
$ perl -le'use open ":std", ":encoding(UTF-8)"; print chr(0x263A);'
☺
$ perl -le'use open ":std", ":encoding(UTF-8)"; print chr(0x00C0);'
À
现在来谈谈“为什么”。
文件句柄只能传输字节,因此除非您另有说明,Perl 文件处理期望字节。这意味着您提供给
print
的字符串只能包含字节,或者换句话说,它不能包含超过 255 个字符。输出正是您提供的:
$ perl -e'print map chr, 0x00, 0x65, 0xC0, 0xF0' | od -t x1
0000000 00 65 c0 f0
0000004
这个很有用。这与您想要的不同,但这并不意味着它是错误的。如果你想要不同的东西,你只需要告诉 Perl 你想要什么。
通过添加
:encoding
层,句柄现在需要一串 Unicode 字符,或者我称之为“文本”。该层告诉 Perl 如何将文本转换为字节。
$ perl -e'
use open ":std", ":encoding(UTF-8)";
print map chr, 0x00, 0x65, 0xC0, 0xF0, 0x263a;
' | od -t x1
0000000 00 65 c3 80 c3 b0 e2 98 ba
0000011
你的权利是
chr
不知道或不关心Unicode。与 length
、substr
、ord
和 reverse
一样,chr
实现基本字符串函数,而不是 Unicode 函数。这并不意味着它不能用于处理文本字符串。正如您所看到的,问题不在于 chr
,而在于构建字符串后对字符串所做的操作。
字符是字符串的元素,字符是数字。这意味着字符串只是一个数字序列。您是否将这些数字视为 Unicode 代码点(文本)、打包 IP 地址或温度测量值完全取决于您以及您将字符串传递到的函数。
以下是一些运算符示例,它们确实为作为操作数接收的字符串分配了含义:
m//
需要一串 Unicode 代码点。connect
需要表示 sockaddr_in
结构的字节序列。print
有句柄但没有 :encoding
需要字节序列。print
带有 :encoding
的句柄,需要 Unicode 代码点序列。那么如何将数字转换为由与其对应的单个字符组成的字符串,以便 real_chr(0xC0) eq 'À' 成立?
chr(0xC0) eq 'À'
确实成立。您是否记得告诉 Perl 您通过 use utf8;
使用 UTF-8 编码了源代码?如果你没有告诉 Perl,Perl 实际上会在 RHS 上看到一个两个字符的字符串。
关于您添加的问题:
encoding
编译指示存在问题。我建议不要使用它。相反,使用
use open ':std', ':encoding(UTF-8)';
这将解决其中一个问题。您遇到的另一个问题是
chr(0x00C0) =~ /\w/
这是一个已知的错误,出于向后兼容性的原因而被故意破坏。也就是说,除非您请求该语言的更新版本,如下所示:
use 5.014; # use 5.012; *might* suffice.
一种早在 5.8 版本就有效的解决方法:
my $x = chr(0x00C0);
utf8::upgrade($x);
$x =~ /\w/
感谢maaartinus指出问题,感谢ikegami提供解决方案;我将使用以下示例添加 EBCDIC 可移植性:
use open ":std", ":encoding(UTF-8)" ; # This fixes line 1
print chr utf8::unicode_to_native 0xe4 ; # line 1 : bytes dec 195 164 ( ie the utf-8 for Unicode U+E4 )
print chr utf8::unicode_to_native 65 ; # line 2 : utf-8 for Unicode U+41
print chr utf8::unicode_to_native 0xc3a4 ; # line 3 : utf-8 for Unicode U+C3A4
my $lx = chr utf8::unicode_to_native 0xe4 ; # $lx holds : bytes dec 195 164 ( ie the utf-8 for Unicode U+E4 )
print $lx ;