【问题标题】:Converting to unicode characters in Perl?在 Perl 中转换为 unicode 字符?
【发布时间】:2014-11-16 07:28:44
【问题描述】:

我想在 Perl 中将文本(印地语)转换为 Unicode。我在 CPAN 中搜索过。但是,我找不到我正在寻找的确切模块/方式。基本上,我正在寻找类似this 的东西。

我的输入是:

इस परीक्षण के लिए है

我的预期输出是:

\u0907\u0938\u0020\u092a\u0930\u0940\u0915\u094d\u0937\u0923\u0020\u0915\u0947\u0020\u0932\u093f\u090f\u0020\u0939\u0948

如何在 Perl 中实现这一点?

给我一​​些建议。

【问题讨论】:

  • 我已经尝试过 Perl 模块列表(EncodeText::Unidecode)。

标签: perl unicode


【解决方案1】:

如果你只想要一个简单的转换器,你可以使用下面的过滤器

perl -CSDA -nle 'printf "\\u%*v04x\n", "\\u",$_'
#or
perl -CSDA -nlE 'printf "\\u%04x",$_ for unpack "U*"'

喜欢:

echo "इस परीक्षण के लिए है" | perl -CSDA -ne 'printf "\\u%*v04x\n", "\\u",$_'
#or
perl -CSDA -ne 'printf "\\u%*v04x\n", "\\u",$_' <<<  "इस परीक्षण के लिए है"

打印:

\u0907\u0938\u0020\u092a\u0930\u0940\u0915\u094d\u0937\u0923\u0020\u0915\u0947\u0020\u0932\u093f\u090f\u0020\u0939\u0948\u000a

带有代理对的Unicode。

use strict;
use warnings;
use utf8;
use open qw(:std :utf8);

my $str = "if( \N{U+1F42A}+\N{U+1F410} == \N{U+1F41B} ){ \N{U+1F602} = \N{U+1F52B} } # ορισμός ";

print "$str\n";
for my $ch (unpack "U*", $str) {
        if( $ch > 0xffff ) {
                my $h = ($ch - 0x10000) / 0x400 + 0xD800;
                my $l = ($ch - 0x10000) % 0x400 + 0xDC00;
                printf "\\u%04x\\u%04x", $h, $l;
        }
        else {
                printf "\\u%04x", $ch;
        }
}
print "\n";

打印

if( ?+? == ? ){ ? = ? } # ορισμός 
\u0069\u0066\u0028\u0020\ud83d\udc2a\u002b\ud83d\udc10\u0020\u003d\u003d\u0020\ud83d\udc1b\u0020\u0029\u007b\u0020\ud83d\ude02\u0020\u003d\u0020\ud83d\udd2b\u0020\u007d\u0020\u0023\u0020\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2\u0020

【讨论】:

  • 此解决方案存在与基本多语言平面之外的 Unicode 字符(即 > U+FFFF)有关的潜在问题,因为您的代码将产生超过四个十六进制数字的转义。详情请见my comment to Oleg G's answer
  • @amon OP 要求印地语,例如字符在 BMP 内部,但您是对的,并且作为参考添加了一个带有代理对的示例......
【解决方案2】:

因为我留下了一些关于其他答案可能达不到各种工具期望的内容,所以我想分享一个解决方案,将基本多语言平面之外的字符编码为两个转义对:"?"会变成\ud83d\ude03

这是由以下人员完成的:

  1. 将字符串编码为 UTF-16,不带字节顺序标记。我们明确地选择了一个字节序。在这里,我们任意使用大端形式。这会产生一串八位字节(“字节”),其中两个八位位组构成一个 UTF-16 代码单元,两个四个八位位组代表一个 Unicode 代码点。

    这样做是为了方便和性能;我们也可以自己确定 UTF-16 代码单元的数值。

  2. unpack将生成的二进制字符串转换为代表每个 UTF-16 代码单元的 16 位整数。我们必须尊重正确的字节序,因此我们对unpack 使用n* 模式(即16 位大字节序无符号整数)。

  3. 将每个代码单元格式化为\uxxxx 转义。

作为一个 Perl 子例程,这看起来像

use strict;
use warnings;
use Encode ();

sub unicode_escape {
    my ($str) = @_;
    my $UTF_16BE_octets = Encode::encode("UTF-16BE", $str);
    my @code_units = unpack "n*", $UTF_16BE_octets;
    return join '', map { sprintf "\\u%04x", $_ } @code_units;
}

测试用例:

use Test::More tests => 3;
use utf8;

is unicode_escpape(''), '',
    'empty string is empty string';

is unicode_escape("\N{SMILING FACE WITH OPEN MOUTH}"), '\ud83d\ude03',
    'non-BMP code points are escaped as surrogate halves';

my $input = 'इस परीक्षण के लिए है';
my $output = '\u0907\u0938\u0020\u092a\u0930\u0940\u0915\u094d\u0937\u0923\u0020\u0915\u0947\u0020\u0932\u093f\u090f\u0020\u0939\u0948';
is unicode_escape($input), $output,
    'ordinary BMP code points each have a single escape';

【讨论】:

    【解决方案3】:

    你真的不需要任何模块来做到这一点。 ord 用于提取字符代码,printf 用于将其格式化为 4 位零填充十六进制就足够了:

    use utf8;
    my $str = 'इस परीक्षण के लिए है';
    (my $u_encoded = $str) =~ s/(.)/sprintf "\\u%04x", ord($1)/sge;
    # \u0907\u0938\u0020\u092a\u0930\u0940\u0915\u094d\u0937\u0923\u0020\u0915\u0947\u0020\u0932\u093f\u090f\u0020\u0939\u0948
    

    【讨论】:

    • 此解决方案存在与基本多语言平面之外的 Unicode 字符(即 > U+FFFF)有关的潜在问题,因为您的代码将产生超过四个十六进制数字的转义。详情请见my comment to the other Oleg's answer
    【解决方案4】:

    试试这个

    use utf8;
    
    my $str = 'इस परीक्षण के लिए है';
    
    for my $c (split //, $str) {
        printf("\\u%04x", ord($c));
    }
    print "\n";
    

    【讨论】:

    • 小心:许多工具或编程语言(如 Java、JavaScript 和 Python)使用的 \uxxxx 表示法仅支持 4 个十六进制数字,这使得这些转义不适合大于 @987654323 的代码点@。常见的解决方法是将文本编码为 UTF-16,然后将每个 16 位整数表示为 \uxxxx 转义(即代理对中的编码)。 Python 还提供了另一种解决方法:32 位 \Uxxxxxxxx 表示法。您的代码可能会产生五位或更多位数的转义,这些转义通常无法识别,例如当应用于$str = "?" (U+1F603 张开嘴的笑脸)。
    猜你喜欢
    • 1970-01-01
    • 2011-03-04
    • 2015-05-01
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多