如何在 Perl 中找到 Unicode 字符串的长度?

Posted

技术标签:

【中文标题】如何在 Perl 中找到 Unicode 字符串的长度?【英文标题】:How do I find the length of a Unicode string in Perl? 【发布时间】:2010-11-22 12:42:37 【问题描述】:

length() 的 perldoc 页面告诉我应该使用 bytes::length(EXPR) 来查找以字节为单位的 Unicode 字符串,或者 bytes 页面会回应这一点。

use bytes;
$ascii = 'Lorem ipsum dolor sit amet';
$unicode = 'Lørëm ípsüm dölör sît åmét';

print "ASCII: " . length($ascii) . "\n";
print "ASCII bytes: " . bytes::length($ascii) . "\n";
print "Unicode: " . length($unicode) . "\n";
print "Unicode bytes: " . bytes::length($unicode) . "\n";

然而,此脚本的输出与手册页不一致:

ASCII: 26
ASCII bytes: 26
Unicode: 35
Unicode bytes: 35

在我看来,length() 和 bytes::length() 对 ASCII 和 Unicode 字符串的返回相同。我的编辑器默认设置为将文件写入为 UTF-8,所以我认为 Perl 将整个脚本解释为 Unicode——这是否意味着 length() 会自动正确处理 Unicode 字符串?

编辑:见我的评论;我的问题没有多大意义,因为在上面的示例中,length() not 工作“正常” - 它以字节而不是字符显示 Unicode 字符串的长度。我最初偶然发现的原因是我需要在 HTTP 消息中设置 Content-Lenth 标头(以字节为单位)的程序。我已经阅读了 Perl 中的 Unicode,并期望必须做一些花哨的事情才能使事情正常进行,但是当 length() 准确地返回了我需要的东西时,我很困惑!有关 Perl 中 use utf8use bytesno bytes 的概述,请参阅已接受的答案。

【问题讨论】:

我不明白你为什么说 length() 可以正确处理 unicode 字符串。在您的示例中,length() 给出与 bytes::length() 相同的结果,即字节数,而不是字符数(这是正确的)。 换句话说,length($unicode) 将字符串解释为 ASCII,而不是 unicode。 你说的完全正确!我完全忽略了这个事实——在我的程序中,我使用 length() 来设置 HTTP 消息中的 Content-Length 标头,该标头需要以字节为单位。在阅读了 length() 文档后,我期待该函数返回一些不正确的东西,但实际上当 Perl 处于 use bytes 模式时,这正是我想要的:Unicode 字符串的长度,以字节为单位,而不是字符。 为什么要一个 Unicode 字符串的长度?你用它做什么? 【参考方案1】:

如果您的脚本以 UTF-8 编码,请使用utf8 pragma。另一方面,bytes pragma 将在长度上强制字节语义,即使字符串是 UTF-8。两者都在当前的词法范围内工作。

$ascii = 'Lorem ipsum dolor sit amet';

    use utf8;
    $unicode = 'Lørëm ípsüm dölör sît åmét';

$not_unicode = 'Lørëm ípsüm dölör sît åmét';

no bytes; # default, can be omitted
print "Character semantics:\n";

print "ASCII: ", length($ascii), "\n";
print "Unicode: ", length($unicode), "\n";
print "Not-Unicode: ", length($not_unicode), "\n";

print "----\n";

use bytes;
print "Byte semantics:\n";

print "ASCII: ", length($ascii), "\n";
print "Unicode: ", length($unicode), "\n";
print "Not-Unicode: ", length($not_unicode), "\n";

这个输出:

Character semantics:
ASCII: 26
Unicode: 26
Not-Unicode: 35
----
Byte semantics:
ASCII: 26
Unicode: 35
Not-Unicode: 35

【讨论】:

【参考方案2】:

bytes pragma 的目的是替换当前作用域中的length 函数(以及其他几个与字符串相关的函数)。因此,程序中对length 的每次调用都是对bytes 提供的length 的调用。这更符合你想要做的:

#!/usr/bin/perl

use strict;
use warnings;

sub bytes($) 
    use bytes;
    return length shift;


my $ascii = "foo"; #really UTF-8, but everything is in the ASCII range
my $utf8  = "\x24d5\x24de\x24de";

print "[$ascii] characters: ", length $ascii, "\n",
    "[$ascii] bytes     : ", bytes $ascii, "\n",
    "[$utf8] characters: ", length $utf8, "\n",
    "[$utf8] bytes     : ", bytes $utf8, "\n";

您的推理中的另一个细微缺陷是存在 Unicode 字节之类的东西。 Unicode 是字符的枚举。例如,它说 U+24d5 是 ⓕ(带圆圈的拉丁小写字母 F);什么 Unicode 没有指定一个字符占用多少字节。那是留给编码的。 UTF-8 表示占用 3 个字节,UTF-16 表示占用 2 个字节,UTF-32 表示占用 4 个字节,依此类推。这里是comparison of Unicode encodings。 Perl 默认使用 UTF-8 作为其字符串。 UTF-8 的好处是前 127 个字符在各方面都与 ASCII 相同。

【讨论】:

【参考方案3】:

我发现可以使用 Encode 模块来影响长度的工作方式。

如果 $string 是 utf8 编码的字符串。

编码::_utf8_on($string); # length 函数将显示之后的代码点数。

编码::_utf8_off($string); # length 函数会在此之后显示字符串中的字节数。

【讨论】:

【参考方案4】:

这里有很多有问题的评论。

Perl 不知道——也不关心——哪些字符串是“Unicode”,哪些不是。它只知道组成字符串的代码点。

查看 Perl 的内部 UTF8 标志表明您可能对 Perl 字符串有错误的想法。例如,“UTF-8 编码字符串”——即像 utf8::encode 这样的编码操作的结果——通常没有设置该标志。

有一些接口存在抽象泄漏,设置了内部 UTF8 标志的字符串的行为与没有该标志的同一组代码点的行为不同(即,在 utf8::downgrade 之后)。依赖这些行为是不明智的,因为 Perl 自己的维护者将它们视为错误。大多数由“unicode_strings”和“unicode_eval”特性修复,其余由 CPAN 的 Sys::Binmode 修复。

【讨论】:

以上是关于如何在 Perl 中找到 Unicode 字符串的长度?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 perl 正则表达式替换命令中使用 unicode 字符?

如何在 Perl 中仅匹配 Unicode 字符串中的完全组合字符?

Perl 和 Unix 如何以相同的顺序对 Unicode 字符串进行排序和排序?

如何在 Perl POD 派生的手册页中使用 Unicode 字符?

如何使用 Perl 的 DBI 处理 unicode?

如何在 perl 正则表达式中组合多个 Unicode 属性?