2009-08-28 14 views
11

Mam ciąg "re\x{0301}sume\x{0301}" (który drukuje w następujący sposób: re & # x0301; sume & # x0301;) i chcę go odwrócić do "e\x{0301}muse\x{0301}r" (e & # x0301; muse & # x0301; r). Nie mogę użyć Perla reverse, ponieważ traktuje on takie znaki jak "\x{0301}" jako osobne znaki, więc otrzymuję "\x{0301}emus\x{0301}er" (& # x0301; emus & # x0301; er). Jak mogę odwrócić ciąg, ale nadal szanować łączące się znaki?Jak mogę odwrócić ciąg zawierający kombinację znaków w Perlu?

Odpowiedz

8

najlepszą odpowiedzią jest użycie Unicode::GCString, as Sinan points out


zmodyfikowałem przykład Chas trochę:

  • Ustaw kodowanie na STDOUT, aby uniknąć ostrzeżeń "szeroki znak w druku";
  • Użyj pozytywny twierdzenie uprzedzoną (i nie tryb retencji separatora) w split (nie działa po 5.10, najwyraźniej, więc usunąłem go)

Jest to w zasadzie to samo, z kilkoma ulepszeniami.

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 
+0

Wow. Lubię perla, ale to podzielone wyrażenie jest dość magiczne. Moją pierwszą myślą była "brutalna siła": wykonaj funkcję, aby zrobić to, co robi podział - zwróć listę ciągów znaków, z których każda stanowi logiczną postać. Jakkolwiek dostaniesz tę listę (wywołaj ją @x), na szczęście oczywiście następuje część join ('', reverse (@x)). – Roboprog

+2

Magiczne? Jak to? To tylko regex bez efektów ubocznych i robi dokładnie to, co widzisz. Jeśli uważasz, że to magia, nie widziałaś prawdziwej czarnej sztuki Perla. Można to nazwać sprytnym (choć nie chciałbym), ale nie jest magiczny. To prawdopodobnie coś, czego nigdy nie używałeś. –

+0

Próbowałem uruchomić ten przykład przy użyciu Perl v5.12.4 i nie działało. Używałem/(\ X)/zamiast tego. Zainteresowana, czy ta odpowiedź działała w poprzednich wersjach Perla, czy też po prostu tęskniliśmy za oczywistością? – Flimm

12

Można użyć \X special escape (mecz non-łączącą charakter i wszystkie z następujących znaków łączących) z split aby zrobić listę grafemami (z pustych strunach między nimi), odwrócić listę grafemami, następnie join je z powrotem razem:

#!/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"; 
+1

Dla tych mylić (jak ja na początku) o tym, dlaczego są puste ciągi między grafemami, to dlatego, że 'split' jest odwrócony: wykorzystuje dane to jest potrzebne jako separator. Pusty sznur to "od" dwóch grafemów. Dopiero włączenie separatora powoduje, że miesza się grafem z "prawdziwym" wynikiem - paczką pustych strun. Alternatywną (i nieco szybszą) metodą uniknięcia tego jest użycie 'm // g' do przechwytywania grafem zamiast:' join '', reverse $ original = ~/(\ X)/g' –

+2

Aby wyjaśnić komentarz Michaela , kiedy używasz nawiasów pamięci w wyrażeniu, które dajesz podzielić, uruchamiasz "tryb zatrzymywania separatora". Odzyskujesz to, co dzieje się między częściami, które dzielisz. Nie musisz tego jednak robić. Wzorzec (? = \ X) robi to samo bez dodatkowych bitów. Nie, że pusty sznur ma tak naprawdę znaczenie dla małych strun. –

+0

Masz prawo zwrócić uwagę na "tryb przechowywania separatora", dziękuję, to było pomocne. Jednak (? = \ X) nie jest równoważny. Jako dowód, następujące dwa przykłady: podzielone/(a) /, "abc" jest równoważne podzielone/(= A)/"abc" i podzielić/(b + c) /, "abbcd" nie jest równoznaczne z split/(? = b + c) /, "abbcd" – Flimm

0

Niektóre z pozostałych odpowiedzi zawierają elementy, które nie działają dobrze. Oto działający przykład testowany w Perlu 5.12 i 5.14. Niewyznaczenie trybu bin spowoduje, że dane wyjściowe będą generować komunikaty o błędach. Użycie pozytywnej asercji z wyprzedzeniem (i bez trybu zatrzymania separatora) w split spowoduje, że dane wyjściowe będą niepoprawne na moim MacBooku.

#!/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"; 
2

Można użyć Unicode::GCString:

Unicode :: GCString traktuje ciąg Unicode jako sekwencja rozbudowanych klastrów grafem zdefiniowane przez standard Unicode załącznika nr 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"; 

wyjściowa:

résumé -> ́emuśer 
résumé -> émusér
Powiązane problemy