2009-08-28 18 views
11

Tengo la cadena "re\x{0301}sume\x{0301}" (que se imprime así: re & # x0301; sume & # x0301;) y quiero revertirla a "e\x{0301}muse\x{0301}r" (e & # x0301; muse & # x0301; r). No puedo usar reverse de Perl porque trata la combinación de caracteres como "\x{0301}" como caracteres separados, así que termino obteniendo "\x{0301}emus\x{0301}er" (& # x0301; emus & # x0301; er). ¿Cómo puedo invertir la cadena pero respetar los caracteres combinados?¿Cómo puedo invertir una cadena que contiene la combinación de caracteres en Perl?

Respuesta

8

La mejor respuesta es utilizar Unicode::GCString, as Sinan points out


he modificado el ejemplo de Chas un poco:

  • Establezca la codificación en STDOUT para evitar advertencias de "caracteres anchos en la impresión";
  • Use una afirmación de búsqueda positiva hacia delante (y no hay modo de retención del separador) en split (no funciona después de 5,10, por lo visto, así que me lo quitó)

Es básicamente el mismo con un par de ajustes.

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. Me gusta Perl, pero esa expresión dividida es bastante mágica. Mi primer pensamiento fue "fuerza bruta": realice una función para hacer lo que hace la división: devuelva una lista de cadenas, cada una de las cuales representa un carácter lógico. Sin embargo, obtienes esa lista (llámala @x), la parte join ('', reverse (@x)) obviamente sigue, afortunadamente. – Roboprog

+2

¿Mágico? ¿Cómo es eso? Es solo una expresión regular sin efectos secundarios y solo hace exactamente lo que ves. Si crees que eso es magia, no has visto las verdaderas artes negras de Perl. Podría llamarlo inteligente (aunque no lo haría), pero no es mágico. Probablemente sea algo que nunca has usado. –

+0

Intenté ejecutar este ejemplo usando Perl v5.12.4 y no funcionó. Usar/(\ X)/en su lugar lo hizo. Fuera de interés, ¿esta respuesta funcionó en versiones anteriores de Perl, o simplemente nos perdimos lo obvio? – Flimm

12

Puede utilizar el \X special escape (que coincida con un carácter que no sea la combinación y todos los siguientes caracteres de combinación) con split a hacer una lista de grafemas (con cadenas vacías entre ellos), invertir la lista de grafemas, entonces join vuelta juntos:

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

Para aquellos confundido (como era en un principio) acerca de por qué existen cadenas vacías entre los grafemas, es porque el 'split' se invierte: utiliza los datos eso es lo que se quiere como separador La cadena vacía es lo que está "entre" dos grafemas. Solo al incluir el separador en el resultado obtendrás los grafemas mezclados con el resultado "real": un montón de cadenas vacías. Un método alternativo (y ligeramente más rápido) que lo evita es usar un 'm // g' para capturar los grafemas en su lugar:' join '', reverse $ original = ~/(\ X)/g' –

+2

Para aclarar el comentario de Michael , cuando utiliza el paréntesis de memoria en una expresión regular que asigna a la división, activa el "modo de retención de separador". Recuperas lo que va entre las partes que estás dividiendo. No es necesario que haga eso sin embargo. El patrón (? = \ X) hace lo mismo sin bits adicionales. No es que la cadena vacía realmente importe tanto para cuerdas pequeñas. –

+0

Tiene razón al señalar "modo de retención de separador", gracias, eso fue útil. Sin embargo, (? = \ X) no es equivalente. Para la prueba, considere estos dos ejemplos: split/(a) /, "abc" no es equivalente a dividir/(? = A) /, "abc" y split/(b + c) /, "abbcd" no es equivalente a split/(? = b + c) /, "abbcd" – Flimm

0

Algunas de las otras respuestas contienen elementos que no funcionan bien. Aquí hay un ejemplo de trabajo probado en Perl 5.12 y 5.14. Si no se especifica el binmode, la salida generará mensajes de error. Usar una aserción positiva de búsqueda anticipada (y ningún modo de retención de separador) en división causará que la salida sea incorrecta en mi 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

Puede utilizar Unicode::GCString:

Unicode :: GCString trata cadena Unicode como una secuencia de grupos de grafema extendidos definido por Unicode estándar Anexo # 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"; 

Salida:

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