2010-02-25 6 views
7

He encontrado algún comportamiento extraño de Perl: el uso de una clase de caracteres de Posix en una expresión regular altera por completo el orden de clasificación de las cadenas resultantes.¿Por qué el uso de una clase de caracteres POSIX en mi patrón de expresiones regulares da resultados inesperados?

Aquí es mi programa de pruebas:

sub namecmp($a,$b) { 
    $a=~/([:alpha:]*)/; 
    # $a=~/([a-z]*)/; 
    $aword= $1; 

    $b=~/([:alpha:]*)/; 
    # $b=~/([a-z]*)/; 
    $bword= $1; 
    return $aword cmp $bword; 
}; 

$_= <>; 
@names= sort namecmp split; 
print join(" ", @names), "\n"; 

Si cambia al uso de [a-z] de la expresión regular comentado de salida, se obtiene el orden normal, lexicográfico. Sin embargo, el POSIX [: alpha:] clase de caracteres produce algún criterio de ordenación raro-culo, de la siguiente manera:

$test_normal 
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cbb 
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cbb 

$test_posix 
aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cbb 
baa bab bac bba bbb bbc bca bcb bcc caa cbb aba abb abc aca acb acc aab aac aaa 

Mi mejor conjetura es que la clase de caracteres POSIX es la activación de algún tipo de material local nunca he oído de y no solicitó. Supongo que la reacción lógica a "doctor, doctor, duele cuando lo hago este!" es, "bueno, no hagas que, entonces!".

Pero, ¿alguien puede decirme qué está pasando aquí, y por qué? Estoy usando Perl 5.10, pero creo que también funciona bajo Perl 5.8.

+7

Hay algunos programadores, que, cuando se enfrentan con una producción que no esperaban, cuya primera reacción es preguntar * * ¿Qué estoy haciendo mal? ** y resolverlo. Luego, están aquellos cuyo primer instinto es preguntar ** ¿Qué está haciendo mal el compilador/intérprete? ** Los que están en la segunda categoría tienen más dificultades para escribir un buen código. –

+4

La razón más común por la que las personas obtienen un comportamiento inesperado es que esperan algo equivocado. –

Respuesta

13

La clase de caracteres [:alpha:] representa caracteres alfanuméricos en las expresiones regulares de Perl, pero los corchetes hacen no significan lo que hacen normalmente en las expresiones regulares. Por lo que necesita:

$a=~/([[:alpha:]]*)/; 

Esto se menciona en perlre:

sintaxis clase

El carácter POSIX

[:class:] 

también está disponible. Tenga en cuenta que los corchetes [ y ] son literales; siempre deben usarse dentro de una expresión de clase de caracteres.

# this is correct: 
$string =~ /[[:alpha:]]/; 

# this is not, and will generate a warning: 
$string =~ /[:alpha:]/; 
6

Debido a que Perl no es compatible con las clases de caracteres POSIX en esta forma. (Uso [[:alpha:]]. Ver @Greg's answer)

Así

[:alpha:] 

se interpreta como una clase de caracteres formada por los caracteres "a", "h", "l", "p" y ":".

Ahora, para las cadenas que no hacen nada contienen [ahlp:]al principio (debido a la *), por ejemplo, "baa" la coincidencia devolverá una cadena vacía. Una secuencia de curso vacía es, por supuesto, más pequeña que cualquier otra cadena, por lo que se organizarán al principio.

8

Lo que está escribiendo no es Perl por ningún tramo de la imaginación. Puede salirse con la suya porque ha desactivado warnings.Si se hubiera usado advertencias, perl lo hubiera dicho

POSIX syntax [: :] belongs inside character classes in regex; marked by <-- HERE in m/([:alpha:] <-- HERE *)/ at j.pl line 4.

POSIX syntax [: :] belongs inside character classes in regex; marked by <-- HERE in m/([:alpha:] <-- HERE *)/ at j.pl line 8.

Imagínese!

Ahora, perl tendría también le dijo:

Illegal character in prototype for main::namecmp : $a,$b at j.pl line 3.

porque, Perl no es C. Perl no tiene prototipos de las funciones de la clase que parecen estar tratando de usar.

Una mejor manera de escribir la misma funcionalidad exacta, en Perl este tiempo, es decir:

use warnings; use strict; 

sub namecmp { 
    my ($aword) = $a =~ /([[:alpha:]]*)/; 
    my ($bword) = $b =~ /([[:alpha:]]*)/; 
    return $aword cmp $bword; 
} 

print join(' ', sort namecmp split ' ', scalar <>), "\n"; 
+0

Meh, funcionó en su mayoría. Al leer los documentos, sorprende que mi "prototipo" haya funcionado. Aunque, tengo que discrepar con su afirmación inicial: lo que escribí * fue * perl, por definición, porque fue aceptado y se ejecutó sin quejarse. – comingstorm

Cuestiones relacionadas