Почему сортировка говорит, что ɛ = e?


25

ɛ(«Латинский эпсилон») - это буква, используемая в некоторых африканских языках, обычно для обозначения гласного звука в английском «кровать». В Юникоде это кодируется как U + 025B, очень отличное от повседневного e.

Однако, если я sortследующее:

eb
ed
ɛa
ɛc

кажется, что sortсчитает ɛи eэквивалентным:

ɛa
eb
ɛc
ed

Что тут происходит? И есть ли способ сделать ɛи eразличен для sortцелей ИНГ?


21
правила сортировки называются «сортировка», если это помогает вашему
поиску в

1
Попробуйте поместить определенное количество eaсмешанных ɛaвнутри текстового файла и отсортировать его. Вы увидите, что он всегда сортирует eaраньше ɛa. Итак, нет, они не считаются равными.
Бакуриу

Может быть, это очевидный момент, но я еще не видел, чтобы это было предложено явно: если вы сортируете слова в $ (sure_african_language), то естественным решением будет установить языковой стандарт в $ (sure_african_language).
Федерико Полони

@FedericoPoloni Очень хороший момент! К сожалению, я не смог найти ни одной локали, созданной для этого языка.
Драконис,

1
@ GermánBouzas Это, в частности, «латинский эпсилон», форма, разработанная для соответствия латинскому алфавиту. Они выглядят примерно так же, но латинский эпсилон - U + 025B, а греческий эпсилон - U + 03B5.
Драконис

Ответы:


67

Нет, он не считает их эквивалентными, они просто имеют одинаковый основной вес. Так что в первом приближении они сортируются одинаково.

Если вы посмотрите на / usr / share / i18n / locales / iso14651_t1_common (который используется в качестве основы для большинства локалей) в системе GNU (здесь с glibc 2.27), вы увидите:

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

e, ɛИ Eимеют тот же первичный вес, eи тот Eже вторичный вес, только третий вес отличает их.

При сравнении строк sort( strcoll()стандартная функция libc используется для сравнения строк) начинается с сравнения первичных весов всех символов и идет только для второго веса, если строки равны первичным весам (и т. Д. С другими весами) ,

Вот как случай, кажется, игнорируется в порядке сортировки в первом приближении. Abсортирует между aaи ac, но Abможет сортировать до или после, в abзависимости от языкового правила (некоторые языки <MIN>до этого, <CAP>как в британском английском, некоторые <CAP>до <MIN>как на эстонском).

Если бы eимел тот же порядок сортировки ɛ, printf '%s\n' e ɛ | sort -uчто, вернул бы только одну строку. Но, как и <BAS>раньше <PCL>, eодни только раньше ɛ . eɛeсортирует после EEE(при вторичном весе), хотя EEEсортирует после eee(для которого нам нужно подняться до третьего веса).

Теперь, если в моей системе с glibc 2.27, я запускаю:

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

Вы заметите, что есть довольно много символов, которые были определены с одинаковыми 4 весами. В частности, наш ɛ имеет тот же вес, что и:

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

И конечно же:

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

Это можно рассматривать как ошибку в GNU libc locales. В большинстве других систем локали гарантируют, что все разные символы имеют разный порядок сортировки в конце. На GNU локалей, это становится еще хуже, так как есть тысячи символов , которые не имеют порядка сортировки и в конечном итоге сортировки же, в результате чего всех видов проблем (например , разбивающиеся comm, join, lsили комки , имеющих недетерминированные заказы ... ), следовательно, рекомендуется использовать LC_ALL=Cдля решения этих проблем .

Как отметил @ninjalj в комментариях, в версии glibc 2.28, выпущенной в августе 2018 года, были некоторые улучшения в этом направлении, хотя в AFAICS все еще есть некоторые символы или элементы сопоставления, определенные с одинаковым порядком сортировки. В Ubuntu 18.10 с glibc 2.28 и в локали en_GB.UTF-8.

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

(почему U + 00B7 считается эквивалентным U + 0387 только в сочетании с L/ l?!).

А также:

$ 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

(по-прежнему более 1 миллиона символов (95% диапазона Unicode, по сравнению с 98% в 2.27), сортировка аналогична другим символам, поскольку порядок их сортировки не определен).

Смотрите также:


3
Это именно то, что я искал! Для полноты, что означает <PCL>? Другие, кажется, Capital, Miniscule и Basic?
Драконис

3
@Draconis, символ сортировки <PCL> # 16 особенный / особенный
Стефан Шазелас

В самом деле, если мы соберем кучу eaи ɛaсмешаем вместе в файл, мы увидим, что sortсортирует все eas перед ɛas.
Бакуриу

2
Начиная с glibc 2.28, кодовая точка
ninjalj

1
@ Cat, извини, я имел ввиду strcoll(), см. редактировать.
Стефан Шазелас

15

мужской род:

   ***  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.

Так что попробуйте: LC_ALL=C sort file.txt


1
Это работает! Но почему языковой стандарт по умолчанию считает эти совершенно разные кодовые точки одинаковыми? Мне любопытно, почему это происходит.
Драконис

@Draconis Что такое «язык по умолчанию»?
Камиль Мачоровский

@KamilMaciorowski Пустое значение переменной среды; Я не уверен, какой локали это соответствует.
Драконис

3
@Draconis, если LC_ALLпусто, sortможет использовать другие LC_*переменные LANGили некоторые файлы конфигурации.
NieDzejkob

1
LC_COLLATEявляется специфичным для сортировки строк, LANGявляется дополнительным.
ShadowRanger

8

Символ not не равен e, но некоторые локали могут собирать эти знаки близко друг к другу при сопоставлении. Причиной этого является специфический язык, но также некоторые исторические или даже политические предпосылки. Например, большинство людей, вероятно, ожидают, что в евро словарь евро приближается к Европе .

В любом случае, чтобы увидеть, какой порядок сортировки вы используете в данный момент locale, вам locale -aбудет представлен список локалей, доступных в системе, и вы сможете изменить параметры сортировки, скажем, Cтолько для одного сеанса сортировки LC_COLLATE=C sort file. Наконец, чтобы увидеть, как разные локали могут сортировать ваш файл, попробуйте

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

Передайте результат в какой-нибудь инструмент greping, чтобы выбрать локаль, которая соответствует вашим потребностям.


Это прекрасное объяснение, но символы кажутся идентичными, а не просто близко друг к другу.
Драконис

1
Нет, они не считаются идентичными. Добавьте eaв файл простую строку, после чего sort -uвы получите и то eaи другое ɛaв выходной. Лучшая стратегия против сопоставления - избегать ( export LC_COLLATE=C). В противном случае многие уродливые вещи будут происходить (например, /tmp/[a-z]в bashбудет соответствовать /tmp/aи , /tmp/Aно не /tmp/Z).
Мосви

@mosvy Да, интересно ... так что они считаются одинаковыми для целей заказа, но не для целей уникальности?
Драконис

они не считаются одинаковыми. смотрите здесь объяснение об этом.
Мосви

1
@ninjalj, это может быть исправлено в диапазонах glibc fnmatch()и regexp, но не в некоторых подобных, bashкоторые реализуют свои диапазоны самостоятельно, используя strcoll(). У ksh93 никогда не было проблемы, потому что его реализация диапазона использует, strcoll()а также проверяет регистр конца диапазона и совпадает только с символами нижнего регистра, если оба конца являются строчными. У диапазонов zsh нет проблемы, поскольку это делается на основе кодовой точки, а не strcoll ().
Стефан Шазелас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.