2009-08-28 4 views
11

J'ai la chaîne "re\x{0301}sume\x{0301}" (qui imprime comme ceci: re & # x0301; sume & # x0301;) et je veux l'inverser à "e\x{0301}muse\x{0301}r" (e & # x0301; muse & # x0301; r). Je ne peux pas utiliser le reverse de Perl car il traite des caractères de combinaison comme "\x{0301}" comme des caractères séparés, donc je finis par obtenir "\x{0301}emus\x{0301}er" (& # x0301; emus & # x0301; er). Comment puis-je inverser la chaîne, mais toujours respecter les caractères de combinaison?Comment puis-je inverser une chaîne contenant des caractères de combinaison en Perl?

Répondre

8

La meilleure réponse est d'utiliser Unicode::GCString, as Sinan points out


J'ai modifié l'exemple de Chas un peu:

  • Définissez le codage sur STDOUT pour éviter les avertissements "caractères larges dans l'impression";
  • Utilisez une assertion avant positive (sans préciser le mode de rétention de séparation) dans split (ne fonctionne pas après 5,10, apparemment, donc je l'ai enlevé)

Il est fondamentalement la même chose avec quelques coups secs.

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. J'aime perl, mais cette expression fractionnée est plutôt magique. Ma première pensée était «force brute»: faites une fonction pour faire ce que fait la division - renvoyez une liste de chaînes dont chaque entrée représente un caractère logique. Cependant vous obtenez cette liste (appelez-la @x), la partie jointe ('', reverse (@x)) suit évidemment, heureusement. – Roboprog

+2

Magique? Comment? C'est juste une regex sans effets secondaires et elle ne fait exactement ce que vous voyez. Si vous pensez que c'est de la magie, vous n'avez pas vu les vrais arts noirs de Perl. Vous pourriez l'appeler intelligent (bien que je ne le ferais pas), mais ce n'est pas magique. C'est probablement juste quelque chose que tu n'as jamais utilisé. –

+0

J'ai essayé d'exécuter cet exemple en utilisant Perl v5.12.4 et cela n'a pas fonctionné. En utilisant/(\ X)/a fait. Par intérêt, cette réponse a-t-elle fonctionné dans les versions précédentes de Perl, ou avons-nous simplement manqué l'évidence? – Flimm

12

Vous pouvez utiliser le \X special escape (correspondant à un caractère non-combinaison et tous les caractères combinant suivants) avec split pour faire une liste des graphèmes (avec des chaînes vides entre eux), inverse la liste des graphèmes, puis les join retour ensemble:

#!/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

Pour les confondre (comme je l'étais au début) de savoir pourquoi il y a des chaînes vides entre les graphèmes, il est parce que le 'split' est inversé: il utilise les données c'est voulu comme séparateur. La chaîne vide est ce qui est "entre" deux graphèmes. C'est seulement en incluant le séparateur dans le résultat que vous obtenez les graphèmes mélangés avec le résultat "réel" - un tas de chaînes vides. Une méthode alternative (et légèrement plus rapide) qui évite cela est d'utiliser un 'm // g' pour capturer les graphèmes à la place:' join ', inverse $ original = ~/(\ X)/g' –

+2

Pour clarifier le commentaire de Michael , lorsque vous utilisez des parenthèses de mémoire dans une regex que vous donnez à split, vous déclenchez le "mode de rétention de séparateur". Vous récupérez la chose qui se passe entre les parties que vous divisez. Vous n'avez pas besoin de faire cela cependant. Le motif (? = \ X) fait la même chose sans bits supplémentaires. Non que la chaîne vide compte vraiment pour les petites chaînes. –

+0

Vous avez raison de souligner "mode de rétention séparateur", merci, cela a été utile. Cependant, (? = \ X) n'est pas équivalent. Pour preuve, considèrent ces deux exemples: split/(a) /, "abc" ne correspond pas à diviser/(? = A) /, "abc" et split/(b + c) /, "abbcd" n'est pas équivalent à split/(? = b + c) /, "abbcd" – Flimm

0

Certaines des autres réponses contiennent des éléments qui ne fonctionnent pas bien. Voici un exemple de travail testé sur Perl 5.12 et 5.14. Si vous ne spécifiez pas binmode, la sortie générera des messages d'erreur. L'utilisation d'une assertion lookahead positive (et pas de mode de rétention de séparateur) dans split entraînera une erreur de sortie sur mon 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"; 
2

Vous pouvez utiliser Unicode::GCString:

Unicode :: GCString traite chaîne Unicode comme une séquence de groupes de graphèmes étendues définies par Unicode standard Annexe # 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"; 

Sortie:

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