O (deve) LC_COLLATE afeta os intervalos de caracteres?

25
O pedido

Collation em LC_COLLATE define não apenas a ordem de classificação dos caracteres individuais, mas também o significado dos intervalos de caracteres . Ou isso? Considere o seguinte snippet:

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

Intuitivamente, B não está em [a-z] , portanto, isso não deve produzir nada. É o que acontece no Ubuntu 8.04 ou 10.04. Mas em algumas máquinas rodando Debian lenny ou squeeze, B é encontrado, porque o intervalo a-z inclui tudo o que está entre a e z na ordem de agrupamento, incluindo as letras maiúsculas B a Z .

Todos os sistemas testados têm o en_US locale gerado. Também tentei variar a localidade: nas máquinas em que B corresponde, o mesmo acontece em todas as localidades disponíveis (principalmente em latim: {en_{AU,CA,GB,IE,US},fr_FR,it_IT,es_ES,de_DE}{iso8859-1,iso8859-15,utf-8} , também chinês), exceto japonês (em qualquer codificação disponível) e C / POSIX .

O que os intervalos de caracteres significam nas expressões regulares quando você excede o ASCII? Por que existe uma diferença entre algumas instalações Debian, por um lado, e outras instalações Debian e Ubuntu, por outro? Como outros sistemas se comportam? Quem está certo e quem deve ter um bug denunciado?

(Observe que estou perguntando especificamente sobre o comportamento de intervalos de caracteres, como [a-z] in en_US locales, principalmente em sistemas baseados em GNU libc. Não estou perguntando como corresponder a letras minúsculas ou a letras minúsculas ASCII .)

Em duas máquinas Debian, uma em que B está em [a-z] e outra em que não é, a saída de LC_COLLATE=en_US locale -k LC_COLLATE é

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

e a saída de LC_COLLATE=en_US.utf8 locale -k LC_COLLATE é

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=2039
collate-codeset="UTF-8"
    
por Gilles 02.07.2011 / 13:33

2 respostas

2

Se você estiver usando algo diferente de C locale, não use intervalos como [a-z] , pois eles são dependentes de localidade e nem sempre fornecem os resultados esperados. Assim como o problema de caso que você já encontrou, algumas localidades tratam os caracteres com diacríticos (por exemplo, á ) da mesma forma que o caractere base (ou seja, a ).

Em vez disso, use uma classe de caracteres nomeados:

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

Isso sempre fornecerá o resultado correto para a localidade. No entanto, você precisa escolher a localidade para refletir o significado do texto de entrada e do teste que você está tentando aplicar.

Por exemplo, se você precisar encontrar um determinado valor de byte, use o C locale, que está sempre disponível:

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

Se isso não funcionar como esperado, é realmente um bug.

    
por 04.07.2011 / 00:27
0

Intervalos em expressões regulares devem observar a configuração de agrupamento. Aqui está o padrão relevante: link (procure por "expressões de intervalo"). Portanto, echo B | LC_COLLATE=en_US grep '[a-z]' deve produzir B dada uma definição razoável da respectiva localidade. Eu não posso explicar por que isso às vezes não funciona para você, mas eu ficaria muito surpreso se eu encontrasse isso em um sistema não antigo que está instalado e configurado corretamente.

    
por 04.07.2011 / 22:57