Преобразование символов Юникода в Perl?


Я хочу преобразовать текст (хинди) в Юникод на Perl. Я искал в CPAN. Но, я не мог найти точный модуль / путь, который я ищу. В принципе, я ищу что-то вроде этого.

Мои входные данные:

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

Мой ожидаемый результат:

u0907u0938u0020u092au0930u0940u0915u094du0937u0923u0020u0915u0947u0020u0932u093fu090fu0020u0939u0948

Как достичь этого в Perl?

Дайте мне несколько советов.

4 3

4 ответа:

Попробуйте это

use utf8;

my $str = 'इस परीक्षण के लिए है';

for my $c (split //, $str) {
    printf("\\u%04x", ord($c));
}
print "\n";

Вам действительно не нужен никакой модуль, чтобы сделать это. ord для извлечения кода char и 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

Если вы хотите только простой конвертер, вы можете использовать следующий фильтр

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

Поскольку я оставил несколько комментариев о том, как другие ответы могут не соответствовать ожиданиям различных инструментов, я хотел бы поделиться решением, которое кодирует символы вне базовой многоязычной плоскости как пары двух эскейпов: "" станет \ud83d\ude03.

Это делается с помощью:

  1. Кодирование строки как UTF-16, без указания порядка байтов. Мы явно выбираем эндианесс. Здесь мы произвольно используем форму большого конца. Это создает строку октетов ("байт"), где два октета образуют одну кодовую единицу UTF-16, а два или четырех октетов представляют собой кодовую точку Юникода.

    Это сделано для удобства и производительности; мы могли бы также определить числовые значения кодовых единиц UTF-16 самостоятельно.

  2. unpackПреобразуя полученную двоичную строку в 16-битные целые числа, представляющие каждый кодовый блок UTF-16. Мы должны уважать правильную конечность, поэтому мы используем шаблон n* для unpack (т. е. 16-битный большой эндиан без знака целое число).

  3. Форматирование каждой единицы кода как \uxxxx escape.

В качестве подпрограммы 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';