如何在Perl v5.24中获取Unicode码点?

5
我希望记录剪切并粘贴到bash作为参数的字符串的十六进制Unicode码点。ord无法实现这一点;ord似乎只适用于ASCII范围内。关于ord的大部分信息都至少有六年或更长时间,且不再相关,因为我正在使用已内置Unicode支持的v5.24。在Python中很容易实现:

for i in unicode(sys.argv[1], 'utf-8'):
    print i.encode("utf_16_be").encode("hex")

这段代码是在bash中执行的。我认为问题出在ord函数本身,它似乎没有更新以支持Unicode。


# ord.pl does not provide the unicode code point for a pasted variable.
use strict;
use warnings;
#use charnames (); #nope.
#use feature 'unicode_strings'; #nope.  Already automatically using as of v5.12.
#use utf8; #nope.
#binmode(STDOUT, ":encoding(UTF-8)"); #nope.

my $arg = "";

foreach $arg  (@ARGV) {
  print $arg . " is " . ord($arg) . " in code.\n";  # seems to me ord is ascii only.
  #utf8::encode($arg);  #nope.
  #print unpack("H*", $arg) . "\n";  #nope.

  #printf "%vX\n", $arg;  #nope.
}

转化为:

david@A8DT01:~/bin$ ord.pl A B C D a b c d \  \\ … —  €
A is 65 in code.
41
B is 66 in code.
42
C is 67 in code.
43
D is 68 in code.
44
a is 97 in code.
61
b is 98 in code.
62
c is 99 in code.
63
d is 100 in code.
64
  is 32 in code.
20
\ is 92 in code.
5c
… is 226 in code.
c3a2c280c2a6
— is 226 in code.
c3a2c280c294
 is 239 in code.
c3afc280c2a8
€ is 226 in code.
c3a2c282c2ac
david@A8DT01:~/bin$

我希望能够在Python中得到与以下输出相同的结果:

david@A8DT01:~/bin$ python code-points.py "ABCDabcd \ … —  €"
0041
0042
0043
0044
0061
0062
0063
0064
0020
005c
0020
2026
0020
2014
0020
f028
0020
20ac
david@A8DT01:~/bin$

尝试使用选项-CSDA运行您的脚本:即perl -CSDA ord.pl A B C D a b c d \ \\ ... —  €。这对我有效。 - Håkon Hægland
2个回答

5

这不是关于ord的问题,而是编码问题。从命令行输入通常会使用UTF-8编码,ord只能处理单个字符,无法处理多字节字符串。您可以使用-CA开关自动解码@ARGV(或者-CSA以便将STDOUT编码为终端),或在脚本中执行解码操作。

use strict;
use warnings;
use Encode;
foreach my $arg (@ARGV) {
  my $decoded = decode 'UTF-8', $arg;
  print $arg . " is " . ord($decoded) . " in code.\n";
}

然而,您的Python脚本正在执行完全不同的操作,它返回的是字符串编码为UTF-16BE的十六进制表示,而不是Unicode字符的十进制序数。在Perl中也可以这样做。
use strict;
use warnings;
use Encode;
foreach my $arg (@ARGV) {
  my $utf16 = encode 'UTF-16BE', decode 'UTF-8', $arg;
  print $arg . " is " . sprintf("%vX", $utf16) . " in code.\n";
}

谢谢你的帮助。正如我所怀疑的那样,ord不支持Unicode。非ord示例确实返回了可用的键盘可用代码点的近似值。即:Cntl-U ####。如果我要在常见的代码点术语中使用它,我将不得不去掉点号,这很容易。一个两(四)字节字符仍然是一个字符,即使它可能有多个字节。由于ord似乎仅限于单个字节,我认为它只适用于ASCII,无论对错。文档需要说明这一点,以便像我这样的新手不会尝试在Unicode中使用它。目前文档没有说明这一点。 - David Weeks
@DavidWeeks 这里有一个例子。我有两个字符串:"\N{U+2603}"和"\N{U+00E2}\N{U+0098}\N{U+0083}"。这些字符串都是有效的代码点序列,且彼此不相等。如果你将第一个字符串编码为UTF-8,或者从UTF-8解码第二个字符串,它们将相等。第二个字符串是在没有设置-CA的情况下从@ARGV中获取的,或者从STDIN中获取而没有解码层等。第一个字符串是你需要对其进行任何操作的单个Unicode字符,例如ord()、length()、regex匹配等。 - Grinnz
@DavidWeeks 我还要补充一点,非 ord 示例实际上并没有打印代码点,它打印的是 UTF-16BE 编码的字节,这在像 @ikegami 的示例中 \N{U+20000} 这种情况下与代码点不匹配。因此,在这种情况下,你的 Python 程序也会出错。如果你想要代码点的十六进制表示,可以使用第一个示例,但是要加上 sprintf('%04X', ord($decoded)) - Grinnz
关于 "As I suspected, ord is not unicode capable.", 这个说法完全正确, 同时又是完全错误的. ord 完全不了解 Unicode; 它只返回字符串中第一个字符的数字. 但是, 如果字符串的第一个字符是 Unicode Code Points 的字符串, 那么 ord 将会愉快地返回它的数字. - ikegami
@ikegami 只是因为原始代码就是这样做的。需求和代码当然是相互矛盾的,但最终似乎我们达成了目标。 - Grinnz
显示剩余2条评论

3

Perl的等价物是

for ucp_str in unicode(sys.argv[1], 'utf-8'):
    print ucp_str.encode("utf_16_be").encode("hex")

use Encode qw( decode encode );

for my $ucp_str (split(//, decode("UTF-8", $ARGV[0]))) {
   say unpack("H*", encode("UTF-16be", $ucp_str));
}

示例:

$ ./a.py aé€♠
0061
00e9
20ac
2660
d840dc00

$ ./a.pl aé€♠
0061
00e9
20ac
2660
d840dc00

但是您要求输出代码点,而这些程序并不是为此而设计的。因此,可以使用以下方法:

use Encode qw( decode_utf8 );

for my $ucp_num (unpack('W*', decode_utf8($ARGV[0]))) {
   say sprintf("%04X", $ucp_num);
}

示例:

$ ./a2.pl aé€♠
0061
00E9
20AC
2660
20000

获取字符串的字符作为字符串:
  • unpack('(a)*', $_)
  • split(//, $_)
将字符串的字符转换为数字:
  • unpack('W*', $_)
  • map { ord($_) } split(//, $_))
将字节串(范围在0x00..0xFF之间的字符)转换为十六进制:
  • unpack('H*', $_)
  • join "", map { sprintf('%02X', $_) } split(//, $_))
通过以十六进制方式查看字符串的字符进行调试:
  • sprintf("%vX", $_)

谢谢ikegami。在您的示例中,我从“say”收到了语法错误,直到我添加了use v5.24;。我以为这不重要,但我错了。啊,新手的生活。这让我达到了我想要做的事情,即确定未知粘贴字符的代码点。 - David Weeks
“say”需要根据say的文档要求使用“use feature qw(say);”。 use v5.24;包括了use feature qw( say );的功能。 为简洁起见,我们从片段中省略了shebang行,use strict;(始终使用),use warnings;(始终使用)和use feature qw( say );(如果需要)。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接