2010-10-20 13 views
5

El siguiente código¿Por qué agregar un sufijo común revierte el orden de clasificación en la configuración regional en_US?

#!/usr/bin/perl 

use strict; 
use warnings; 

my $s1 = '[email protected]'; 
my $s2 = '[email protected]'; 
my $s3 = 'aaa2000'; 
my $s4 = 'aaa_2000'; 

no locale; 

print "\nNO Locale:\n\n"; 

if ($s1 gt $s2) {print "$s1 is > $s2\n";} 
if ($s1 lt $s2) {print "$s1 is < $s2\n";} 
if ($s1 eq $s2) {print "$s1 is = $s2\n";} 

if ($s3 gt $s4) {print "$s3 is > $s4\n";} 
if ($s3 lt $s4) {print "$s3 is < $s4\n";} 
if ($s3 eq $s4) {print "$s3 is = $s4\n";} 

use locale; 

print "\nWith 'use locale;':\n\n"; 

if ($s1 gt $s2) {print "$s1 is > $s2\n";} 
if ($s1 lt $s2) {print "$s1 is < $s2\n";} 
if ($s1 eq $s2) {print "$s1 is = $s2\n";} 

if ($s3 gt $s4) {print "$s3 is > $s4\n";} 
if ($s3 lt $s4) {print "$s3 is < $s4\n";} 
if ($s3 eq $s4) {print "$s3 is = $s4\n";} 

imprime

NO Locale: 

[email protected] is < [email protected] 
aaa2000 is < aaa_2000 

With 'use locale;': 

[email protected] is > [email protected] 
aaa2000 is < aaa_2000 

la que no puedo seguir: en el mismo tiempo, en condiciones de uso local, hay una < b Y un @ yahoo. com> [email protected]? !!

¿Me falta algo más o menos obvio, o es esto un error? ¿Pueden otros confirmar que ven el mismo comportamiento?

Locale is $ locale 
LANG=en_US.UTF-8 
LC_CTYPE="en_US.UTF-8" 
LC_NUMERIC="en_US.UTF-8" 
LC_TIME="en_US.UTF-8" 
LC_COLLATE="en_US.UTF-8" 
LC_MONETARY="en_US.UTF-8" 
LC_MESSAGES="en_US.UTF-8" 
LC_PAPER="en_US.UTF-8" 
LC_NAME="en_US.UTF-8" 
LC_ADDRESS="en_US.UTF-8" 
LC_TELEPHONE="en_US.UTF-8" 
LC_MEASUREMENT="en_US.UTF-8" 
LC_IDENTIFICATION="en_US.UTF-8" 
LC_ALL= 

Gracias de antemano.

Respuesta

4

Con las configuraciones regionales habilitadas, la intercalación se realiza en varias pasadas. Cada personaje tiene cuatro pesos, que se comparan en pases sucesivos. Los signos @ y _, como la mayoría de los signos de puntuación, no tienen un peso primario, secundario o terciario, por lo que solo entran en juego en el cuarto pase. Así que, por su ejemplo

[email protected] > [email protected] 

en el primer paso, que es realmente la comparación

aaa2000yahoocom = aaa2000yahoocom 

y luego en el cuarto pase (no hay factores de diferenciación en el segundo y tercer pases)

@. > [email protected] 

porque @ es mayor que _ en esta localidad. (Esta es solo una opción que hace la definición del lugar, presumiblemente basada en algún estándar ISO u otro).

Puede ver los detalles de implementación de esto. Una comparación local habilitada se implementa en la biblioteca C como strxfrm(A) cmp strxfrm(B).Ejecutar este programa:

use POSIX; 

my $s1 = '[email protected]'; 
my $s2 = '[email protected]'; 

foreach ($s1, $s2) { 
    printf "%s =>\t%v02x\n", $_, POSIX::strxfrm($_); 
} 

me sale:

[email protected] => 0c.0c.0c.04.02.02.02.24.0c.13.1a.1a.0e.1a.18.01.08.08.08.08.08.08.08.08.08.08.08.08.08.08.08.01.02.02.02.02.02.02.02.02.02.02.02.02.02.02.02.01.08.5d.06.44 
# explanation:   a a a 2 0 0 0 y a h o o c o m DIV secondary weights ...      DIV tertiary weights ...      DIV @  . 
[email protected] => 0c.0c.0c.04.02.02.02.24.0c.13.1a.1a.0e.1a.18.01.08.08.08.08.08.08.08.08.08.08.08.08.08.08.08.01.02.02.02.02.02.02.02.02.02.02.02.02.02.02.02.01.04.36.05.5d.06.44 
# explanation:   a a a 2 0 0 0 y a h o o c o m DIV secondary weights ...      DIV tertiary weights ...      DIV _  @  . 

La forma en que estos números se derivan es un detalle de implementación; solo tienen que salir de forma que una comparación de bytes produzca el resultado final deseado. Pero el concepto es el mismo en todos los entornos de programación modernos con clasificación habilitada para la configuración regional.

+0

Acaba de describir el Algoritmo de intercalación Unicode. No es realmente una cosa local. Pero confiaría en la UCA con o sin modificaciones locales un millón de veces más de lo que nunca confiaría en las configuraciones regionales de los proveedores. Esos siempre me fallan. Considero/opino que las configuraciones regionales son cintas vencidas obsoletas para propósitos de ctype/cotejo ahora que tenemos Unicode. – tchrist

+0

No, estoy describiendo la realidad de su sistema tal como se aplica a su problema. Sospecho que la mayoría de los proveedores hoy en día implementan sus configuraciones regionales basadas en el UCA; después de todo, el UCA no se inventó de la nada. Pero no entiendo por qué los entornos locales fallan o deben considerarse heredados. Ciertamente, necesita reglas de clasificación específicas del idioma siempre que haya idiomas que se desvíen de los pedidos "predeterminados". Pero eso es realmente además del objetivo de esta pregunta. –

2

Obtengo los mismos resultados en mi sistema Linux de 32 bits con la configuración regional en_US.utf8. No es un error de Perl, como se ilustra en este programa C:

#include <locale.h> 
#include <string.h> 
#include <stdio.h> 

void transformed(const char* str) 
{ 
    char dest[256]; 
    const char* c; 

    strxfrm(dest, str, sizeof(dest)); 
    printf("%18s =", str); 
    for (c = dest; *c; ++c) printf(" %02x", *c); 
    puts(""); 
} /* end transformed */ 

void test_strings(const char* s1, const char* s2) 
{ 
    int c = strcoll(s1, s2); 

    printf("%s is %s %s\n", s1, ((c < 0) ? "<" : ((c == 0) ? "=" : ">")), s2); 
} /* end test_strings */ 

int main(int argc, char* argv[]) 
{ 
    puts("with C locale:"); 

    test_strings("[email protected]", "[email protected]"); 
    test_strings("aaa2000", "aaa_2000"); 

    setlocale(LC_ALL, ""); 
    puts("\nwith your locale:"); 

    test_strings("[email protected]", "[email protected]"); 
    test_strings("aaa2000", "aaa_2000"); 
    puts(""); 
    transformed("[email protected]"); 
    transformed("[email protected]"); 
    transformed("aaa2000"); 
    transformed("aaa_2000"); 
    return 0; 
} /* end main */ 

Con LANG=en_US.utf8, genera:

with C locale: 
[email protected] is < [email protected] 
aaa2000 is < aaa_2000 

with your locale: 
[email protected] is > [email protected] 
aaa2000 is < aaa_2000 

[email protected] = 0c 0c 0c 04 02 02 02 24 0c 13 1a 1a 0e 1a 18 01 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 01 08 5d 06 44 
[email protected] = 0c 0c 0c 04 02 02 02 24 0c 13 1a 1a 0e 1a 18 01 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 01 04 36 05 5d 06 44 
      aaa2000 = 0c 0c 0c 04 02 02 02 01 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 
      aaa_2000 = 0c 0c 0c 04 02 02 02 01 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 01 04 36 

La función strxfrm (que se puede acceder en Perl a través del módulo POSIX) devuelve una cadena que indica el orden de clasificación. Cuando compara dos cadenas transformadas byte por byte, la primera que tiene un byte más pequeño es lo primero en el orden de clasificación.

No estoy seguro de si esto es un error o no. Parece que no puedo encontrar ninguna documentación sobre cómo se supone que funciona el orden de colación en_US. Si es un error, está en su biblioteca de C o en la base de datos de configuración regional.

+0

Suena como un error, posiblemente uno intencional conocer los desarrolladores glibc ... –

+0

Sospecho que el problema mencionado anteriormente está relacionado con el siguiente: en un archivo simple que contiene 2 registros con 2 campos separados TAB, como 'a_2 2/a2 1' un comando como 'ordenar -k 1 archivo | cut -f 1 'mostrará un orden inverso al que se muestra con el mismo tipo pero en un archivo que no tiene el segundo campo. – Krambambuli

Cuestiones relacionadas