Чому сорт каже, що ɛ = e?


25

ɛ("Латинська epsilon") - літера, яка використовується в певних африканських мовах, як правило, для позначення голосного звуку в англійській мові "bed". У Unicode він закодований як U + 025B, дуже відмінний від повсякденного e.

Однак, якщо я sortнаступне:

eb
ed
ɛa
ɛc

здається, що sortвважає ɛі eрівнозначним:

ɛa
eb
ɛc
ed

Що тут відбувається? І чи є спосіб зробити ɛта eрозрізнити їх для sortцілей?


21
правила сортування називають «звіряння», якщо це допомагає вашому погуглити
BlueRaja - Денні Pflughoeft

1
Спробуйте помістити певну кількість eaзмішаних із ɛaтекстовим файлом всередині і відсортуйте. Ви побачите , що він завжди сортує eaдо того ɛa. Отже, жодні вони не вважаються рівними.
Бакуріу

Це може бути очевидним моментом, але я ще не бачив, що це прямо пропонується: якщо ви сортуєте слова в $ (some_african_language), природним завданням є встановлення мови в $ (some_african_language).
Федеріко Полоні

@FedericoPoloni Дуже вдалий момент! На жаль, мені не вдалося знайти жодної мови, створеної для цієї мови.
Драконіс

1
@ GermánBouzas Це спеціально "латинський епсілон", форма, призначена для того, щоб відповідати латинському алфавіту. Вони виглядають приблизно однаково, але латинський епсілон - це U + 025B, тоді як грецький epsilon - 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. У більшості інших систем локалі гарантують, що в кінці різних символів різний порядок сортування. На 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

.

Дивись також:


3
Це саме те, що я шукав! Для повноти, що означає <PCL>? Інші, здається, "Капітал", "Незначне" та "Основне"?
Драконіс

3
@Draconis, зіставляючи-символ <PCL> # 16 Particulier / властиво
Стефан Chazelas

Дійсно, якщо ми помістимо купу файлів eaі ɛaзмішаємо їх у файл, ми побачимо, що це sortсортує всі eas до ɛas.
Бакуріу


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

Символ ɛ не дорівнює e, але деякі локалі можуть зібрати ці знаки близько один від одного при зіставленні. Причиною цього є специфіка мови, але також певне історичне чи навіть політичне походження. Наприклад, більшість людей, напевно, очікують, що євро валюта наблизиться до Європи за словником.

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

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

Отримайте результат до якогось інструменту зібрання, щоб вибрати локаль, який відповідає вашим потребам.


Це чудове пояснення, але символи, здається, вважаються однаковими, а не просто близько один до одного.
Драконіс

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

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

вони не вважаються однаковими. дивіться тут пояснення щодо цього.
mosvy

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