Как изменить строку, содержащую комбинации символов в Perl?

I have the the string "re\x{0301}sume\x{0301}" (which prints like this: résumé) and я want to reverse it to "e\x{0301}muse\x{0301}r" (émusér). я can't use Perl reverse because it treats combining characters like "\x{0301}" as separate characters, so я wind up getting "\x{0301}emus\x{0301}er" ( ́emuśer). How can я reverse the string, but still respect the combining characters?

Ответ 1

Лучший ответ - использовать Unicode::GCString, как указывает Синан


Я немного изменил пример Chas:

  • Установите кодировку на STDOUT, чтобы избежать предупреждений "широкий символ в печати";
  • Используйте положительное утверждение вида (и режим хранения разделителя) в split (не работает после 5.10, по-видимому, поэтому я удалил его)

Это в основном то же самое с паролем настроек.

use strict;
use warnings;

binmode STDOUT, ":utf8";

my $original = "re\x{0301}sume\x{0301}";
my $wrong    = reverse $original;
my $right    = join '', reverse split /(\X)/, $original;

print <<HERE;
original: [$original]
   wrong: [$wrong]
   right: [$right]
HERE

Ответ 2

Вы можете использовать \X специальный escape (совпадение с несовместимым символом и всеми перечисленными ниже комбинациями символов) с split, чтобы составить список графем (с пустыми строками между ними), перевернуть список графем, затем join их обратно вместе:

#!/usr/bin/perl

use strict;
use warnings;

my $original = "re\x{0301}sume\x{0301}";
my $wrong    = reverse $original;
my $right    = join '', reverse split /(\X)/, $original;
print "original: $original\n",
      "wrong:    $wrong\n",
      "right:    $right\n";

Ответ 3

Вы можете использовать Unicode::GCString:

Unicode:: GCString обрабатывает строку Unicode как последовательность расширенных кластеров графемы, определенных стандартным стандартом Unicode № 29 [UAX # 29].

#!/usr/bin/env perl

use utf8;
use strict;
use warnings;
use feature 'say';
use open qw(:std :utf8);

use Unicode::GCString;

my $x = "re\x{0301}sume\x{0301}";
my $y = Unicode::GCString->new($x);
my $wrong = reverse $x;
my $correct = join '', reverse @{ $y->as_arrayref };

say "$x -> $wrong";
say "$y -> $correct";

Вывод:

résumé -> ́emuśer
résumé -> émusér

Ответ 4

Perl6 :: Str->reverse также работает.

В случае строки résumé вы также можете использовать основной модуль Unicode::Normalize, чтобы изменить строку на полностью составленную форму (NFC или NFKC) перед reverse ing; однако это не общее решение, потому что некоторые комбинации базового символа и модификатора не имеют предварительно составленной кодовой точки Unicode.

Ответ 5

Некоторые из других ответов содержат элементы, которые не работают. Вот рабочий пример, протестированный на Perl 5.12 и 5.14. Невозможность указать binmode приведет к выходу сообщений об ошибках. Использование положительного утверждения заголовка (и никакого режима хранения разделителя) в split приведет к тому, что вывод будет неправильным на моем Macbook.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'unicode_strings';

binmode STDOUT, ":utf8";

my $original = "re\x{0301}sume\x{0301}";
my $wrong    = reverse $original;
my $right    = join '', reverse split /(\X)/, $original;
print "original: $original\n",
      "wrong:    $wrong\n",
      "right:    $right\n";