Perl ord和chr使用unicode

时间:2012-09-05 23:48:29

标签: perl unicode

令我恐惧的是,我发现chr不能使用Unicode,虽然它确实是手册页几乎是清晰的 p>

  

返回字符集中该NUMBER表示的字符。例如,chr(65)"是" A"无论是ASCII还是Unicode,chr(0x263a)都是Unicode笑脸。

确实,我可以使用

打印笑脸
perl -e 'print chr(0x263a)'

chr(0x00C0)之类的内容不起作用。我看到我的perl v5.10.1有点古老,但是当我在源代码中粘贴各种奇怪的字母时,一切都很好。

我尝试过有趣的事情,例如use utf8use encoding 'utf8',我还没有尝试像use v5.12use 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)不同。而且,前者是一个单词构成字符(正确!),而后者不是(但应该是!)。

1 个答案:

答案 0 :(得分:11)

首先,

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字符,或者我称之为“text”。该层告诉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的权利。与lengthsubstrordreverse一样,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/