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?
Как изменить строку, содержащую комбинации символов в Perl?
Ответ 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";