Perl ord 和 chr 使用 unicode

问题描述 投票:0回答:2

令我惊恐的是,我刚刚发现

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 unicode
2个回答
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 字符,或者我称之为“文本”。该层告诉 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/

0
投票

感谢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 ; 
© www.soinside.com 2019 - 2024. All rights reserved.