Por que a classificação diz que ɛ = e?

25

ɛ ("Latin epsilon") é uma letra usada em certas línguas africanas, geralmente para representar o som da vogal na "cama" inglesa. No Unicode, ele é codificado como U + 025B, muito diferente do dia-a-dia e .

No entanto, se eu sort , o seguinte:

eb
ed
ɛa
ɛc

parece que sort considera ɛ e e equivalente:

ɛa
eb
ɛc
ed

O que está acontecendo aqui? E existe uma maneira de tornar ɛ e e distintos para% de propósitos desort?

    
por Draconis 26.10.2018 / 18:32

3 respostas

68

Não, não os considera equivalentes, eles apenas têm o mesmo peso primário. Então, na primeira aproximação, eles ordenam o mesmo.

Se você olhar para / usr / share / i18n / locales / iso14651_t1_common (como é usado como base para a maioria das localidades) em um sistema GNU (aqui com o glibc 2.27), você verá:

<U0065> <e>;<BAS>;<MIN>;IGNORE # 259 e
<U025B> <e>;<PCL>;<MIN>;IGNORE # 287 ɛ
<U0045> <e>;<BAS>;<CAP>;IGNORE # 577 E

e , ɛ e E têm o mesmo peso primário, e e E mesmo peso secundário, apenas o terceiro peso os diferencia.

Ao comparar strings, sort (a função strcoll() padrão libc é usada para comparar strings) começa comparando os pesos primários de todos os caracteres, e só vai para o segundo peso se as strings forem iguais aos pesos primários (e assim por diante com os outros pesos).

É assim que o caso parece ser ignorado na ordem de classificação na primeira aproximação. Ab classifica entre aa e ac , mas Ab pode classificar antes ou depois de ab dependendo da regra de idioma (alguns idiomas têm <MIN> antes <CAP> como em inglês britânico, alguns <CAP> antes de <MIN> como em estoniano).

Se e tivesse a mesma ordem de classificação que ɛ , printf '%s\n' e ɛ | sort -u retornaria apenas uma linha. Mas como <BAS> classifica antes de <PCL> , e classifica antes ɛ . eɛe classifica após EEE (no peso secundário) mesmo que EEE ordene após eee (para o qual precisamos subir para o terceiro peso).

Agora, se no meu sistema com o glibc 2.27, eu corro:

sed -n 's/\(.*;[^[:blank:]]*\).*//p' /usr/share/i18n/locales/iso14651_t1_common |
  sort -k2 | uniq -Df1

Você notará que existem alguns caracteres que foram definidos exatamente com os mesmos 4 pesos. Em particular, nosso ɛ tem os mesmos pesos que:

<U01DD> <e>;<PCL>;<MIN>;IGNORE
<U0259> <e>;<PCL>;<MIN>;IGNORE
<U025B> <e>;<PCL>;<MIN>;IGNORE

E com certeza:

$ printf '%s\n' $'\u01DD' $'\u0259' $'\u025B' | sort -u
ǝ
$ expr ɛ = ǝ
1

Isso pode ser visto como um bug das localidades GNU libc. Na maioria dos outros sistemas, as localidades garantem que todos os diferentes caracteres tenham uma ordem de classificação diferente no final. Nas localidades GNU, fica ainda pior, pois existem milhares de caracteres que não têm uma ordem de classificação e acabam classificando os mesmos, causando todos os tipos de problemas (como comm , join , ls ou% globs tendo ordens não-determinísticas ...), daí a recomendação de usando LC_ALL=C para contornar essas questões .

Como foi observado por @ninjalj nos comentários, o glibc 2.28 lançado em agosto de 2018 veio com algumas melhorias nessa frente, apesar do AFAICS, ainda existem alguns caracteres ou elementos de agrupamento definidos com ordem de classificação idêntica. No Ubuntu 18.10 com glibc 2.28 e em uma localidade en_GB.UTF-8.

$ expr $'L\ub7' = $'L\u387'
1

(por que U + 00B7 seria considerado equivalente como U + 0387 somente quando combinado com L / l ?!).

E:

$ perl -lC -e 'for($i=0; $i<0x110000; $i++) {$i = 0xe000 if $i == 0xd800; print chr($i)}' | sort > all-chars-sorted
$ uniq -d all-chars-sorted | wc -l
4
$ uniq -D all-chars-sorted | wc -l
1061355

(ainda mais de 1 milhão de caracteres (95% do intervalo Unicode, abaixo de 98% em 2,27) classificando o mesmo que outros caracteres, pois sua ordem de classificação não está definida).

Veja também:

por 26.10.2018 / 21:38
15

man sort:

   ***  WARNING  ***  The locale specified by the environment affects sort
   order.  Set LC_ALL=C to get the traditional sort order that uses native
   byte values.

Então, tente: LC_ALL=C sort file.txt

    
por 26.10.2018 / 18:35
8

O caractere ɛ não é igual a e, mas algumas localidades podem reunir esses sinais juntos durante o agrupamento. A razão para isso é específica do idioma, mas também algum histórico ou mesmo histórico político. Por exemplo, a maioria das pessoas provavelmente espera que a moeda € uro se aproxime da Europa no dicionário.

De qualquer forma, para ver o agrupamento que você está usando, execute locale , o locale -a fornecerá a lista de localidades disponíveis no sistema e altere a declaração de agrupamento para C apenas para uma execução de classificação LC_COLLATE=C sort file . Finalmente, para ver como diferentes localidades podem classificar seu arquivo, tente

for loc in $(locale -a)
    do echo ____"${loc}"____
    LC_COLLATE="$loc" sort file
done

canalize o resultado para alguma ferramenta de greping para escolher uma localidade que atenda às suas necessidades.

    
por 26.10.2018 / 19:34