LC_COLLATE afeta (deve) os intervalos de caracteres?

27

A ordem de intercalaçãoLC_COLLATE define não apenas a ordem de classificação de caracteres individuais, mas também o significado dos intervalos de caracteres. Ou faz? Considere o seguinte snippet:

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

Intuitivamente, Bnão está presente [a-z], portanto, isso não deve gerar nada. É o que acontece no Ubuntu 8.04 ou 10.04. Mas em algumas máquinas rodando Debian Lenny ou aperto, Bé encontrado, porque a gama a-zinclui tudo o que está entre ae zna ordem de agrupamento, incluindo as letras maiúsculas Batravés Z.

Todos os sistemas testados têm o en_UScódigo do idioma gerado. Também tentei variar o código do idioma: nas máquinas com as Bcorrespondências acima, o mesmo acontece em todos os códigos de idioma disponíveis (principalmente com base em latim: {en_{AU,CA,GB,IE,US},fr_FR,it_IT,es_ES,de_DE}{iso8859-1,iso8859-15,utf-8}também códigos de idioma chinês), exceto no japonês (em qualquer codificação disponível) e C/ POSIX.

O que significam intervalos de caracteres em expressões regulares quando você ultrapassa 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 se comportam outros sistemas? Quem está certo e contra quem um bug deve ser relatado?

(Observe que estou perguntando especificamente sobre o comportamento dos intervalos de caracteres, como [a-z]nas en_USlocalidades, principalmente nos sistemas baseados em GNU libc. Não estou perguntando como combinar letras minúsculas ou letras minúsculas ASCII.)


Em duas máquinas Debian, uma onde Bestá [a-z]e outra onde não está, 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"
Gilles 'SO- parar de ser mau'
fonte
11
Não se reproduz em uma instância do Debian Lenny que tive útil. en_USPorém, não verificou se é gerado.
alex
11
@alex Se o código do idioma não for gerado, o código do Cidioma será usado como substituto, e sua ordem de intercalação será de valores de bytes diretos, portanto B, não será correspondida. Teste em um código de idioma que aparece na saída de locale -a.
Gilles 'SO- stop be evil'
11
Observe que en_US NÃO é o mesmo que en_US.utf8 e geralmente significa en_US.iso-8859-1, dependendo exatamente do que você instalou. Se en_US (sem sufixo) não aparecer na saída do código do idioma -a, você realmente não tem esse código do idioma. O que LC_COLLATE = pt_US locale -k LC_COLLATE mostra?
Neil Mayhew
11
Desde então, isso surgiu em uma questão prática, e não teórica: por que as letras maiúsculas são incluídas em um intervalo de letras minúsculas em um regex awk?
Caleb
11
@isaac Infelizmente, sete anos depois, parece que não tenho acesso a nenhum sistema problemático. Todos foram atualizados ou desativados.
Gilles 'SO- stop be evil'

Respostas:

3

Se você estiver usando algo diferente do Ccódigo do idioma, não deverá usar intervalos como, [a-z]pois eles dependem do código do idioma e nem sempre fornecem os resultados esperados. Além do problema de caso que você já encontrou, algumas localidades tratam caracteres com sinais diacríticos (por exemplo, á ) da mesma forma que o caractere base ( por exemplo, a ).

Em vez disso, use uma classe de caracteres nomeada:

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

Isso sempre dará o resultado correto para o código do idioma. No entanto, você precisa escolher o local para refletir o significado do texto de entrada e do teste que você está tentando aplicar.

Por exemplo, se você precisar encontrar um valor de byte específico, use o Ccódigo do idioma, que está sempre disponível:

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

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

Neil Mayhew
fonte
Eu sei disso, não foi o que eu pedi. Estou perguntando especificamente sobre o que significa um intervalo explícito e por que diferentes distribuições (mesmo com GNU libc e GNU grep) têm comportamentos diferentes. (Downvoted porque mesmo que o que você diz é correto, é irrelevante.)
Gilles 'SO parada sendo mal'
11
O que quero dizer é que o significado de um intervalo explícito depende da localidade, e não são necessários sistemas diferentes para definir suas localidades da mesma maneira, portanto, isso não é um erro. Tecnicamente, você está abusando do sistema, para não se surpreender ao obter um comportamento "indefinido". Além disso, várias pessoas comentaram que não podem reproduzir o comportamento em seus sistemas Debian, então parece haver algo incomum em seus sistemas.
Neil Mayhew
11
Eu sei que o comportamento dos intervalos depende dos locais. Estou perguntando como, e surpreso, que diferentes sistemas usando o Glibc (e, ao que parece, até diferentes instalações da mesma versão do Debian) tenham comportamentos diferentes. Eu adicionei a saída de locale -kà minha pergunta; é idêntico em duas máquinas Debian, uma onde Bestá no intervalo e outra onde não está. BTW eu não sou root em nenhuma máquina (então não é algo peculiar que eu faço como administrador).
Gilles 'SO- stop be evil'
echo "Baü" | LC_COLLATE=C grep -o '[[:lower:]]'retorna aAND üenquanto echo "Baü" | LC_COLLATE=C grep -o '[a-z]'retorna apenas a. A meu ver, "inferior" não é realmente o que o OP queria #
Daniel Alder
Meu argumento original ainda permanece: não use intervalos, a menos que você esteja no Clocal. Acredito que isso seja relevante para o OP, que estava procurando relatar um bug. Se você não estiver no Clocal, os resultados do uso de intervalos são altamente imprevisíveis e, portanto, nunca podem ser considerados um bug. Por outro lado, se você precisar encontrar um valor de byte específico, use o Ccódigo do idioma. Meu ponto secundário foi que, se você realmente deseja procurar letras minúsculas em um código de idioma, use uma classe de caracteres. Embora o OP possa não estar procurando isso, outros poderão encontrar essa pergunta.
Neil Mayhew
1

Intervalos em expressões regulares devem observar a configuração de agrupamento. Aqui está o padrão relevante: http://pubs.opengroup.org/onlinepubs/007908799/xbd/re.html (procure por "expressões de intervalo"). Portanto, echo B | LC_COLLATE=en_US grep '[a-z]'deve ser Bfornecida uma definição sensata do respectivo código do idioma. Não sei explicar por que isso às vezes não funciona para você, mas ficaria muito surpreso se encontrasse isso em um sistema não antigo que esteja instalado e configurado corretamente.

Peter Eisentraut
fonte
11
echo B | LC_COLLATE=en_US.utf8 grep '[a-z]' Não imprime nada no Ubuntu 12.04 com grep 2.10. Não imprime nada no Centos 6.5 com grep 2.6.3. Funciona no Debian 6.0.8 com grep 2.6.3.
Ian D. Allen