【问题标题】:How to get unicode code points in perl v5.24?如何在 perl v5.24 中获取 unicode 代码点?
【发布时间】:2019-10-08 02:19:54
【问题描述】:

我想记录作为参数剪切并粘贴到 bash 中的字符串的十六进制 unicode 代码点。 ord 不这样做; ord 似乎只能在 ascii 范围内工作。

我发现的关于 ord 的大部分内容至少有 6 年或更久,并且不再相关,因为我使用的是 v5.24,我读过它内置了 unicode 支持。 在 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 \ \\ … —  €。它对我有用。

标签: perl unicode ord


【解决方案1】:

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", $_)

【讨论】:

  • 谢谢池上。在您的示例中,我从“say”中得到了一个语法错误,直到我添加了use v5.24; 我认为没关系,我错了。啊,菜鸟的生活。这让我得到了我打算做的事情,即确定未知粘贴字符的代码点。
  • say 需要use feature qw( say );,如say 的文档中所述。 use v5.24; 包含use feature qw( say ); 的功能。为简洁起见,我们从 sn-ps 中省略了 shebang 行 use strict;(总是使用)、use warnings;(总是使用)和 use feature qw( say );(如果需要)。
【解决方案2】:

不是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。 non-ord 示例确实返回了键盘可用代码点的可用近似值。 IE:Cntl-U ####。如果我在参考通用代码点用语时使用它,我将不得不很容易地丢失这个点。一个两(到四)字节字符仍然是一个字符,即使它可能有多个字节。由于 ord 似乎仅限于单个字节,因此我仅将其称为 ascii,无论对错。文档需要这么说,这样像我这样的菜鸟就不会尝试将它与 unicode 一起使用。文档目前没有这样说。
  • @DavidWeeks 这不准确。它完全支持 unicode。 ord("\N{SNOWMAN}") 将返回雪人字符的序号。问题是你对 Perl 的字符串模型,或者 Unicode 和字符编码之间的区别,或者两者都有误解。编码的字符串仍然只是一个字节串,需要解码才能用作字符串。这最好在边缘完成,例如在读取或写入时,或通过手柄上的层。
  • @DavidWeeks 这是一个例子。我有两个字符串:“\N{U+2603}”和“\N{U+00E2}\N{U+0098}\N{U+0083}”。这些字符串都是有效的代码点系列,并且彼此不相等。如果您将第一个字符串编码为 UTF-8,或者将第二个字符串从 UTF-8 解码,它将等于另一个。第二个字符串是您在没有设置-CA 的情况下在@ARGV 中获得的内容,或者从没有解码层的STDIN 获得的内容等。第一个字符串是您需要对其进行任何操作的单个unicode 字符,例如ord( )、length()、正则表达式匹配等。
  • @DavidWeeks 我还要补充一点,non-ord 示例实际上并没有打印代码点,它打印的是 UTF-16BE 编码字节,在 @987654329 等情况下与代码点不匹配@ 就像在@ikegami 的例子中一样。因此,在这种情况下,您的 python 程序也会出错。如果您想要代码点的十六进制表示,请使用第一个示例,但使用 sprintf('%04X', ord($decoded))
  • Re "正如我所怀疑的, ord 不支持 unicode。",同时完全正确和完全错误。 ord 对 Unicode 一无所知;它只是返回字符串的第一个字符作为它的数字。但是,如果字符串的第一个字符是一串 Unicode 代码点,那么ord 会很高兴地返回它的数字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2010-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-09
相关资源
最近更新 更多