【问题标题】:Perl ord and chr working with unicodePerl ord 和 chr 使用 unicode
【发布时间】:2012-08-30 18:03:13
【问题描述】:

令我惊恐的是,我刚刚发现 chr 不适用于 Unicode,尽管它可以做一些事情。手册页几乎很清楚

返回字符集中由该 NUMBER 表示的字符。例如,chr(65)" 在 ASCII 或 Unicode 中都是“A”,而 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)不同。而且,前者是单词组成字符(正确!),而后者不是(但应该是!)。

【问题讨论】:

  • @D.Shawley:Linux 2.6.32-42-generic,x86_64 GNU/Linux,Ubuntu 10.4,所以 utf8 是原生的
  • Á 的 UTF8 八位字节序列是 C3 81 C1 是 ISO-8859-1 代码点。我的 Perlfu 有点弱,或者我会提出一个答案。
  • 一些文档在这方面很薄弱,但是 UTF-8 实现,即使在后面的 Perl 5.10.1 中也相当强大。在你在 Perl 中使用 Unicode 之前,我建议阅读 perlunitutperluniintro。在您的情况下,chr 不是问题,而是您没有为 UTF-8 编码和解码字符串。如果您要输出 UTF-8(或任何其他编码),您的字符串需要先在输出时转换为八位字节。
  • @ikegami:删除它会将输出更改为eq1 - eq1; match1 - no_match2。所以我有两个相等的字符串,其中只有一个匹配。
  • 在 Perl 5.14 之前的任何东西中对 Unicode 正则表达式的支持都被破坏了。在 Perl 5.14 中,您的第二个正则表达式在没有 use encoding 和附加 /u 修饰符的情况下被修复。见Character set modifiers in perlre

标签: perl unicode


【解决方案1】:

首先,

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,你有权。与lengthsubstrordreverse 一样,chr 实现了基本的字符串函数,而不是 Unicode 函数。这并不意味着它不能用于处理文本字符串。如您所见,问题不在于chr,而在于您在构建字符串后对其所做的操作。

字符是字符串的元素,字符是数字。这意味着字符串只是一个数字序列。是否将这些数字视为 Unicode 代码点(文本)、打包 IP 地址或温度测量值完全取决于您和您将字符串传递给的函数。

以下是一些运算符的示例,它们为作为操作数接收的字符串赋予含义:

  • m// 需要一串 Unicode 代码点。
  • connect 需要表示 sockaddr_in 结构的字节序列。
  • 带有不带 :encoding 句柄的 print 需要一个字节序列。
  • 带有:encoding 句柄的print 需要一个Unicode 代码点序列。

那么如何将数字转换为由与其对应的单个字符组成的字符串,以便例如 real_chr(0xC0) eq 'À' 成立?

chr(0xC0) eq 'À' 确实成立。您是否记得告诉 Perl 您使用 UTF-8 编码了您的源代码,使用 use utf8;?如果你不告诉 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/

【讨论】:

  • 如果你想要一个更手动的解决方案,你也可以use Encode qw( encode ); print encode(chr(0x00c0)) 与上面的 ":encoding(UTF-8)" 做几乎相同的事情,但没有 I/O 句柄。
  • @zostay,应该是 encode('UTF-8', chr(0x00c0))encode_utf8(chr(0x00c0)),但是是的。
  • @ikegami:你对 IO 肯定是对的,但我不关心 IO,因为它只是一个调试输出。除了ASCII,我不会输出任何东西,这就是我忽略警告的原因。我仍然不明白发生了什么,请参阅我的编辑。
  • @maaartinus,已添加问题的答案。请注意,5.14 是受支持的最旧的 Perl 版本。
  • @ikegami:就是这样,我的版本我做不到use 5.014,但utf8::upgrade 帮了忙!
猜你喜欢
  • 1970-01-01
  • 2012-04-13
  • 2022-12-04
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
  • 2020-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多