¿LC_COLLATE (debería) afectar los rangos de caracteres?

27

El orden de LC_COLLATEclasificación define no solo el orden de clasificación de los caracteres individuales, sino también el significado de los rangos de caracteres. O lo hace? Considere el siguiente fragmento:

unset LANGUAGE LC_ALL
echo B | LC_COLLATE=en_US grep '[a-z]'

Intuitivamente, Bno está dentro [a-z], por lo que esto no debería generar nada. Eso es lo que sucede en Ubuntu 8.04 o 10.04. Sin embargo, en algunas máquinas que ejecutan Lenny Debian o apretón, Bse encuentra, ya que el rango a-zincluye todo lo que está entre ay zen el orden de clasificación, incluyendo las mayúsculas Ba través Z.

Todos los sistemas probados tienen la en_USconfiguración regional generada. También intenté variar la configuración regional: en las máquinas en las que Bse hace coincidir anteriormente, sucede lo mismo en todas las configuraciones regionales disponibles (en su mayoría basadas en latín {en_{AU,CA,GB,IE,US},fr_FR,it_IT,es_ES,de_DE}{iso8859-1,iso8859-15,utf-8}, también en configuraciones regionales chinas), excepto en japonés (en cualquier codificación disponible) y C/ POSIX.

¿Qué significan los rangos de caracteres en las expresiones regulares , cuando vas más allá de ASCII? ¿Por qué hay una diferencia entre algunas instalaciones de Debian por un lado, y otras instalaciones de Debian y Ubuntu por el otro? ¿Cómo se comportan otros sistemas? ¿Quién tiene razón y contra quién se debe informar un error?

(Tenga en cuenta que estoy preguntando específicamente sobre el comportamiento de los rangos de caracteres, como [a-z]en las en_USconfiguraciones regionales, principalmente en los sistemas basados ​​en GNU libc. No estoy preguntando cómo hacer coincidir letras minúsculas o minúsculas ASCII).


En dos máquinas Debian, una donde Bestá adentro [a-z]y otra donde no está, la salida de LC_COLLATE=en_US locale -k LC_COLLATEes

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=1
collate-codeset="ISO-8859-1"

y la salida de LC_COLLATE=en_US.utf8 locale -k LC_COLLATEes

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=2039
collate-codeset="UTF-8"
Gilles 'SO- deja de ser malvado'
fuente
1
No se reproduce en una instancia de Debian Lenny que he tenido a mano. Sin en_USembargo, no se verificó si se genera.
alex
1
@alex Si el entorno local no se genera, el Centorno local se utiliza como reserva y su orden de clasificación es de valores de bytes directos, por Blo que no coincidirá. Pruebe en un entorno local que aparezca en la salida de locale -a.
Gilles 'SO- deja de ser malvado'
1
Tenga en cuenta que en_US NO es lo mismo que en_US.utf8, y generalmente significa en_US.iso-8859-1, dependiendo exactamente de lo que haya instalado. Si en_US (sin sufijo) no aparece en la salida de locale -a, en realidad no tiene esta locale. ¿Qué muestra LC_COLLATE = en_US locale -k LC_COLLATE?
Neil Mayhew
1
Esto se ha convertido en una pregunta práctica más que teórica aquí: ¿por qué se incluyen las letras mayúsculas en una gama de letras minúsculas en una expresión regular awk?
Caleb
1
@isaac Desafortunadamente, 7 años después, parece que no tengo acceso a ningún sistema problemático. Todos han sido actualizados o dados de baja.
Gilles 'SO- deja de ser malvado'

Respuestas:

3

Si está utilizando otra cosa que no sea la Cconfiguración regional, no debería utilizar rangos como [a-z]estos, ya que dependen de la configuración regional y no siempre dan los resultados esperados. Además del problema de caso que ya ha encontrado, algunos entornos locales tratan los caracteres con signos diacríticos (por ejemplo, á ) igual que el carácter base (es decir, a ).

En su lugar, use una clase de caracteres con nombre:

echo B | grep '[[:lower:]]'

Esto siempre dará el resultado correcto para la configuración regional. Sin embargo, debe elegir la configuración regional para reflejar el significado tanto del texto de entrada como de la prueba que está intentando aplicar.

Por ejemplo, si necesita encontrar un valor de byte particular, use la Cconfiguración regional, que siempre está disponible:

echo B | LANG=C grep '[a-z]'

Si esto no funciona como se esperaba, realmente es un error.

Neil Mayhew
fuente
Lo sé, no es lo que pregunté. Me pregunto específicamente qué significa un rango explícito y por qué las diferentes distribuciones (incluso con GNU libc y GNU grep) tienen comportamientos diferentes. (Downvoted porque a pesar de lo que usted dice es correcto, es irrelevante.)
Gilles 'SO siendo parada del mal'
1
Mi punto es que el significado de un rango explícito depende de la configuración regional, y no se requieren diferentes sistemas para definir sus configuraciones regionales de la misma manera, por lo que esto no es un error. Técnicamente, está abusando del sistema, por lo que no debería sorprenderse al obtener un comportamiento "indefinido". Además, varias personas han comentado que no pueden reproducir el comportamiento en sus sistemas Debian, por lo que parece haber algo inusual en sus sistemas.
Neil Mayhew el
1
Sé que el comportamiento de los rangos depende de las configuraciones regionales. Me pregunto cómo y me sorprende que los diferentes sistemas que usan Glibc (y resulta que incluso las diferentes instalaciones de la misma versión de Debian) tengan comportamientos diferentes. He agregado el resultado de locale -kmi pregunta; Es idéntico en dos máquinas Debian, una donde Bestá dentro del rango y otra donde no está. Por cierto, no soy root en ninguna de las máquinas (por lo que no es algo peculiar que hago como administrador).
Gilles 'SO- deja de ser malvado'
echo "Baü" | LC_COLLATE=C grep -o '[[:lower:]]'devuelve aY ümientras que echo "Baü" | LC_COLLATE=C grep -o '[a-z]'solo regresa a. En mi opinión, "bajar" no es realmente lo que quería el OP
Daniel Alder
Sin embargo, mi punto original sigue en pie: no use rangos a menos que esté en la Cconfiguración regional. Creo que esto es relevante para el OP, que estaba buscando reportar un error. Si no está en la Cconfiguración regional, los resultados del uso de rangos son altamente impredecibles y, por lo tanto, nunca se pueden considerar un error. Por otro lado, si necesita encontrar un valor de byte particular, simplemente use la Cconfiguración regional. Mi punto secundario fue que si realmente desea buscar letras minúsculas en una configuración regional, use una clase de caracteres. Aunque el OP puede no haber estado buscando esto, otros podrían hacerlo si encuentran esta pregunta.
Neil Mayhew
1

Los rangos en expresiones regulares deben observar la configuración de intercalación. Aquí está el estándar relevante: http://pubs.opengroup.org/onlinepubs/007908799/xbd/re.html (busque "expresiones de rango"). Por echo B | LC_COLLATE=en_US grep '[a-z]'lo tanto, debería Bdar una definición razonable de la configuración regional respectiva. No puedo explicar por qué esto a veces no funciona para ti, pero me sorprendería mucho si me encontrara con un sistema no antiguo que esté instalado y configurado correctamente.

Peter Eisentraut
fuente
1
echo B | LC_COLLATE=en_US.utf8 grep '[a-z]' No imprime nada en Ubuntu 12.04 con grep 2.10. No imprime nada en Centos 6.5 con grep 2.6.3. Funciona en Debian 6.0.8 con grep 2.6.3.
Ian D. Allen