कसरी लिनक्सको क्रमले स्ट्रिङहरू क्रमबद्ध गर्दछ

परिचय

यो सबै एक छोटो लिपिको साथ सुरु भयो जुन ठेगाना जानकारी संयोजन गर्नु पर्ने थियो ई-मेल сотрудников, полученных из списка пользователей почтовой рассылки, с должностями сотрудников, полученными из базы отдела кадров. Оба списка были экспортированы в текстовые файлы в кодировке Юникод UTF-8 и сохранены с юниксовскими концами строк.

सामाग्री mail.txt

Иванов Андрей;[email protected]

सामाग्री buhg.txt

Иванова Алла;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Абаканов Михаил;маляр

मर्ज गर्न, फाइलहरू युनिक्स आदेशद्वारा क्रमबद्ध गरिएको थियो भाग्य र युनिक्स कार्यक्रमको इनपुटमा पेश गरियो सामेल, которая неожиданно завершилась с ошибкой:

$> sort buhg.txt > buhg.srt
$> sort mail.txt > mail.srt
$> join buhg.srt mail.srt > result
join: buhg.srt:4: is not sorted: Иванов Андрей;слесарь

आफ्नो आँखाले क्रमबद्ध गर्ने नतिजा हेर्दा, सामान्यतया, क्रमबद्ध सही छ, तर पुरुष र महिला उपनामको संयोगको मामलामा, महिलाहरू पुरुषहरू भन्दा अगाडि आउँछन्:

$> sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванова Алла;маляр
Иванов Андрей;слесарь

युनिकोडमा क्रमबद्ध त्रुटि जस्तो देखिन्छ वा क्रमबद्ध एल्गोरिदममा नारीवादको अभिव्यक्ति जस्तो देखिन्छ। पहिलो, निस्सन्देह, अधिक प्रशंसनीय छ।

यसलाई अहिलेको लागि बन्द गरौं सामेल र फोकस गर्नुहोस् भाग्य। वैज्ञानिक पोकिङ प्रयोग गरेर समस्या समाधान गर्ने प्रयास गरौं। पहिले, बाट लोकेल परिवर्तन गरौं en_ मा मा रु_आरयू। क्रमबद्ध गर्न, यो वातावरण चर सेट गर्न पर्याप्त हुनेछ LC_COLLATE, но мы не будем мелочиться:

$> LANG=ru_RU.UTF-8 sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванова Алла;маляр
Иванов Андрей;слесарь

केही परिवर्तन भएन।

Попробуем перекодировать файлы в однобайтовую кодировку:

$> iconv -f UTF-8 -t KOI8-R buhg.txt 
 | LANG=ru_RU.KOI8-R sort 
 | iconv -f KOI8-R -t UTF8

फेरि केहि परिवर्तन भएको छैन।

Ничего не поделаешь, придётся искать решение в интернете. Прямо про русские фамилии ничего нет, но есть вопросы про другие странности сортировки. Вот, например, такая проблема: unix sort treats ‘-‘ (dash) characters as invisible. Если кратко, то строки "a-b", "aa", "ac" сортируются как "aa", "a-b", "ac".

जवाफ जताततै मानक छ: प्रोग्रामर लोकेल प्रयोग गर्नुहोस् "C" र तिमी खुशी हुनेछौ। प्रयास गराैँ:

$> LANG=C sort buhg.txt
Ёлкина Элла;крановщица
Абаканов Михаил;маляр
Иванов Андрей;слесарь
Иванова Алла;адвокат

केही परिवर्तन भएको छ। इभानोभहरू सही क्रममा लाइनमा थिए, यद्यपि योल्किना कतै चिप्लिए। मूल समस्यामा फर्कौं:

$> LANG=C sort buhg.txt > buhg.srt
$> LANG=C sort mail.txt > mail.srt
$> LANG=C join buhg.srt mail.srt > result

Сработало без ошибок, как и обещал интернет. И это несмотря на Ёлкину в первой строке.

समस्या हल भएको जस्तो देखिन्छ, तर केवल मामला मा, अर्को रूसी एन्कोडिङ प्रयास गरौं - विन्डोज CP1251:

$> iconv -f UTF-8 -t CP1251 buhg.txt 
 | LANG=ru_RU.CP1251 sort 
 | iconv -f CP1251 -t UTF8 

क्रमबद्ध परिणाम, अनौठो रूपमा पर्याप्त, लोकेलसँग मेल खान्छ "C", र सम्पूर्ण उदाहरण, तदनुसार, त्रुटि बिना चल्छ। एक प्रकारको रहस्यवाद।

मलाई प्रोग्रामिङमा रहस्यवाद मन पर्दैन किनभने यसले प्राय: गल्तीहरू लुकाउँछ। हामीले कसरी काम गर्छ भनेर गम्भीरतापूर्वक हेर्नुपर्नेछ। भाग्य र यसले के असर गर्छ? LC_COLLATE .

В конце я попытаюсь ответить на вопросы:

  • почему неправильно сортировались женские фамилии
  • किन LANG=ru_RU.CP1251 बराबर भएको देखियो LANG=C
  • почему у भाग्य и सामेल разные представления о порядке отсортированных строк
  • почему во всех моих примерах есть ошибки
  • अन्तमा कसरी आफ्नो मनपर्ने तार क्रमबद्ध गर्न

युनिकोडमा क्रमबद्ध गर्दै

Первой остановкой будет технический отчёт № 10 под названием Unicode collation algorithm अनलाइन unicode.org. Отчёт содержит много технических деталей, так что я позволю себе привести краткое изложение основных идей.

Collation - "तुलना" स्ट्रिङ कुनै पनि क्रमबद्ध एल्गोरिथ्म को आधार हो। एल्गोरिदमहरू आफैं फरक हुन सक्छन् ("बबल", "मर्ज", "फास्ट"), तर तिनीहरू सबैले तिनीहरू देखा पर्ने क्रम निर्धारण गर्न स्ट्रिङको जोडीको तुलना प्रयोग गर्नेछन्।

Сортировка строк на естественном языке — это довольно сложная проблема. Даже в простейших однобайтовых кодировках порядок букв в алфавите, хоть в чём-то отличающемся от английской латиницы, уже не будет совпадать с порядком числовых значений, которыми эти буквы кодируются. Так в немецком алфавите буква Ö बीच खडा छ О и P, а в кодировке CP850 она попадает между ÿ и Ü.

तपाइँ एक विशिष्ट एन्कोडिङबाट सार गर्न प्रयास गर्न सक्नुहुन्छ र "आदर्श" अक्षरहरू विचार गर्न सक्नुहुन्छ जुन युनिकोडमा गरिएको छ, केहि क्रममा व्यवस्थित गरिएको छ। इन्कोडिङहरू UTF8, UTF16 वा एक बाइट कोइई ran-r (यदि युनिकोडको सीमित उपसमूह आवश्यक छ) ले अक्षरहरूको विभिन्न संख्यात्मक प्रतिनिधित्वहरू दिनेछ, तर आधार तालिकाको एउटै तत्वहरूलाई सन्दर्भ गर्दछ।

Оказывается, что даже построив таблицу символов с нуля, мы не сможем назначить в ней универсальный порядок символов. В различных национальных алфавитах, использующих одинаковые буквы, порядок этих букв может отличаться. Например, во французском языке Æ будет считаться лигатурой и сортироваться как строка AE। नर्वेजियन मा Æ एक अलग अक्षर हुनेछ, जुन पछि स्थित छ Z। खैर, जस्तै ligatures को अतिरिक्त Æ существуют буквы, записываемые несколькими символами. Так в чешском алфавите есть буква Ch, जो बीचमा खडा छ H и I.

Кроме различия в алфавитах существуют и иные национальные традиции, влияющие на сортировку. В частности возникает вопрос: в каком порядке должны следовать в словаре слова, состоящие из прописных и строчных букв? Также на сортировку могут повлиять особенности использования знаков препинания. В испанском языке в начале вопросительного предложения ставится перевёрнутый вопросительный знак (तिमीलाई संगित मन पर्छ?)। यस अवस्थामा, यो स्पष्ट छ कि सोधपुछ वाक्यहरूलाई वर्णमाला बाहिर छुट्टै क्लस्टरमा समूहबद्ध गर्नु हुँदैन, तर कसरी अन्य विराम चिन्हहरूसँग रेखाहरू क्रमबद्ध गर्ने?

Я не буду останавливаться на сортировке строк в языках сильно отличающихся от европейских. Отмечу, что в языках с направлением письма справа налево или сверху вниз символы в строках, скорее всего, хранятся в порядке чтения, и даже в неалфавитных письменностях есть свои способы посимвольного упорядочения строк. Например, иероглифы могут упорядочиваться по начертанию (चिनियाँ अक्षर कुञ्जीहरू) или по произношению. Как должны упорядочиваться эмодзи, я, честно говоря, не представляю, но и для них можно что-нибудь придумать.

माथि सूचीबद्ध सुविधाहरूको आधारमा, युनिकोड तालिकाहरूमा आधारित स्ट्रिङहरू तुलना गर्नका लागि आधारभूत आवश्यकताहरू तयार गरिएको थियो:

  • स्ट्रिङ तुलना कोड तालिकामा क्यारेक्टरहरूको स्थितिमा निर्भर गर्दैन;
  • последовательности символов, образующих единый символ, приводятся к каноническому виду (A + शीर्ष सर्कल जस्तै छ Å);
  • स्ट्रिङहरू तुलना गर्दा, स्ट्रिङको सन्दर्भमा क्यारेक्टरलाई विचार गरिन्छ र आवश्यक भएमा, यसको छिमेकीहरूसँग तुलनाको एक एकाइमा जोडिन्छ (Ch в чешском) или разбивается на несколько (Æ फ्रान्सेली मा);
  • सबै राष्ट्रिय सुविधाहरू (अक्षर, ठूलो/लोअरकेस, विराम चिह्न, लेखन प्रकारहरूको क्रम) आदेशको म्यानुअल असाइनमेन्ट (इमोजी) सम्म कन्फिगर गरिनुपर्छ;
  • तुलना क्रमबद्ध गर्नका लागि मात्र होइन, अन्य धेरै ठाउँहरूमा पनि महत्त्वपूर्ण छ, उदाहरणका लागि पङ्क्ति दायराहरू निर्दिष्ट गर्नका लागि ({A... z} in बाश);
  • तुलना चाँडै गर्नुपर्छ।

थप रूपमा, रिपोर्टका लेखकहरूले तुलनात्मक गुणहरू तयार गरे जुन एल्गोरिथ्म विकासकर्ताहरूले भर पर्नु हुँदैन:

  • तुलना एल्गोरिथ्मलाई प्रत्येक भाषाको लागि क्यारेक्टरहरूको छुट्टै सेटको आवश्यकता पर्दैन (रूसी र युक्रेनी भाषाहरूले धेरै सिरिलिक क्यारेक्टरहरू साझा गर्छन्);
  • तुलना युनिकोड तालिकाहरूमा वर्णहरूको क्रममा भर पर्नु हुँदैन;
  • вес строки не должен являться атрибутом строки, поскольку одна и та же строка в разных культурных контекстах может иметь различные веса;
  • веса строк могут меняться при слиянии или разбиении (из x < y त्यो पालन गर्दैन xz < yz);
  • एउटै तौल भएका विभिन्न तारहरूलाई क्रमबद्ध गर्ने एल्गोरिदमको दृष्टिकोणबाट बराबर मानिन्छ। त्यस्ता स्ट्रिङहरूको थप क्रम प्रस्तुत गर्न सम्भव छ, तर यसले कार्यसम्पादनलाई घटाउन सक्छ;
  • при повторных сортировках строки, имеющие одинаковые веса, могут меняться местами. Устойчивость — это свойство конкретного алгоритма сортировки, а не свойство алгоритма сравнения строк (см. предыдущий пункт);
  • правила сортировки могут меняться со временем по мере уточнения/изменения культурных традиций.

यो पनि निर्धारित गरिएको छ कि तुलना एल्गोरिथ्मले प्रशोधन भइरहेको स्ट्रिङ को अर्थशास्त्र को बारे मा केहि थाहा छैन। तसर्थ, अंकहरू मात्र समावेश भएका स्ट्रिङहरूलाई सङ्ख्याको रूपमा तुलना गरिनु हुँदैन, र अंग्रेजी नामहरूको सूचीमा लेख (बीटल्स, द).

यी सबै आवश्यकताहरू पूरा गर्नको लागि, एक बहु-स्तर (वास्तवमा चार-स्तर) तालिका क्रमबद्ध एल्गोरिदम प्रस्ताव गरिएको छ।

पहिले, स्ट्रिङका क्यारेक्टरहरूलाई क्यानोनिकल फारममा घटाइन्छ र तुलनाको एकाइहरूमा समूहबद्ध गरिन्छ। तुलनाको प्रत्येक एकाइलाई तुलनाको धेरै स्तरहरूसँग सम्बन्धित धेरै वजनहरू तोकिएको छ। तुलनात्मक एकाइहरूको तौलहरू क्रमबद्ध सेटका तत्वहरू हुन् (यस अवस्थामा, पूर्णांकहरू) जसलाई बढी वा कमको लागि तुलना गर्न सकिन्छ। विशेष अर्थ बेवास्ता गरियो (0x0) означает, что на соответствующем уровне сравнения данная единица в сравнении не участвует. Сравнение строк может повторяться несколько раз, с использованием весов соответствующих уровней. На каждом из уровней веса единиц сравнения двух строк последовательно сравниваются между собой.

विभिन्न राष्ट्रिय परम्पराहरूको लागि एल्गोरिथ्मको विभिन्न कार्यान्वयनहरूमा, गुणांकहरूको मान फरक हुन सक्छ, तर युनिकोड मानकले तौलहरूको आधारभूत तालिका समावेश गर्दछ - "Default Unicode Collation Element Table" (DUCET). Хочу заметить, что установка переменной LC_COLLATE фактически является указанием на выбор таблицы весов в функции сравнения строк.

वजन गुणांक DUCET निम्नानुसार व्यवस्थित:

  • पहिलो स्तरमा, सबै अक्षरहरू एउटै अवस्थामा घटाइन्छ, डायक्रिटिकहरू खारेज गरिन्छ, विराम चिन्हहरू (सबै होइन) बेवास्ता गरिन्छन्;
  • दोस्रो स्तरमा, केवल diacritics खातामा लिइन्छ;
  • तेस्रो स्तरमा, केवल मामलालाई ध्यानमा राखिएको छ;
  • चौथो स्तरमा, विराम चिह्नलाई मात्र ध्यानमा राखिन्छ।

Сравнение происходит в несколько проходов: сначала сравниваются коэффициенты первого уровня; если веса совпали, то проходит повторное сравнение с весами второго уровня; затем, возможно, третьего и четвёртого.

तुलना समाप्त हुन्छ जब पङ्क्तिहरूमा विभिन्न तौलहरूसँग तुलनाको मिल्दो एकाइहरू समावेश हुन्छन्। चारै तहमा बराबर तौल भएका पङ्क्तिहरूलाई एकअर्काको बराबर मानिन्छ।

Вот этот алгоритм (с кучей дополнительных технических деталей) и дал название отчёту № 10 — "युनिकोड कोलेसन एल्गोरिथ्म" (ACU).

यहाँ हाम्रो उदाहरणबाट क्रमबद्ध व्यवहार अलि स्पष्ट हुन्छ। यो युनिकोड मानक संग तुलना गर्न राम्रो हुनेछ।

कार्यान्वयन परीक्षण गर्न ACU त्यहाँ एक विशेष छ प्रश्नोत्तरी, использующий файл весов, कार्यान्वयन DUCET. В файле весов можно найти разные забавности. Например, там есть порядок костяшек маджонга и европейского домино, а также порядок мастей в колоде карт (символ 1F000 र थप)। कार्ड सूटहरू ब्रिजको नियमहरू अनुसार राखिएको छ - PCBT, र सूटमा कार्डहरू अनुक्रम T, 2,3, XNUMX... K मा छन्।

म्यानुअल रूपमा पङ्क्तिहरू अनुसार क्रमबद्ध गरिएको छ भनेर जाँच गर्दै DUCET धेरै थकाइपूर्ण हुनेछ, तर, सौभाग्य देखि हाम्रो लागि, युनिकोड संग काम गर्न को लागी पुस्तकालय को एक उदाहरणीय कार्यान्वयन छ - "International Components for Unicode"(आईसीयू).

На сайте этой библиотеки, разработанной в आईबीएम, есть демонстрационные странички, в том числе и स्ट्रिङ तुलना एल्गोरिथ्म पृष्ठ। हामी हाम्रो परीक्षण लाइनहरू पूर्वनिर्धारित सेटिङहरूसँग प्रविष्ट गर्छौं र, हेर र हेर, हामीले उत्तम रूसी क्रमबद्ध पाउँछौं।

Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Иванова Алла;адвокат

वैसे, वेबसाइट आईसीयू можно найти уточнение работы алгоритма сравнения при обработке знаков препинания. В примерах कोलेसन FAQ игнорируются апостроф и дефис.

युनिकोडले हामीलाई मद्दत गर्यो, तर अनौठो व्यवहारको कारणहरू खोज्नुहोस् भाग्य в लिनक्स कतै जानु पर्नेछ।

glibc मा क्रमबद्ध गर्दै

उपयोगिता स्रोत कोडहरूको द्रुत दृश्य भाग्य बाट GNU कोर उपयोगिताहरू показал, что в самой утилите локализация сводится к печати текущего значения переменной LC_COLLATE при запуске в отладочном режиме:

$ sort --debug buhg.txt > buhg.srt
sort: using ‘en_US.UTF8’ sorting rules

स्ट्रिङ तुलनाहरू मानक प्रकार्य प्रयोग गरेर प्रदर्शन गरिन्छ strcoll, а значит всё интересное находится в библиотеке glibc.

मा wiki परियोजना glibc स्ट्रिङ तुलना गर्न समर्पित एउटा अनुच्छेद. Из этого абзаца можно понять, что в glibc क्रमबद्ध एल्गोरिदममा आधारित छ जुन पहिले नै हामीलाई थाहा छ ACU (The Unicode collation algorithm) र/वा यसको नजिकको मानकमा आईएसओ 14651 (अन्तर्राष्ट्रिय स्ट्रिङ क्रम र तुलना). По поводу последнего стандарта следует заметить, что на сайте standards.iso.org आईएसओ 14651 официально объявлен общедоступным, но соответствующая ссылка ведёт на несуществующую страницу. Гугл выдаёт несколько страниц со ссылками на официальные сайты, которые предлагают купить электронную копию стандарта за сотню евро, но на третей-четвёртой странице поисковой выдачи найдутся и прямые ссылки на पीडीएफ. В целом, стандарт практически не отличается от ACU, तर पढ्न धेरै बोरिंग छ किनभने यसले स्ट्रिङ क्रमबद्ध गर्ने राष्ट्रिय विशेषताहरूको स्पष्ट उदाहरणहरू समावेश गर्दैन।

मा सबैभन्दा रोचक जानकारी wiki त्यहाँ लिङ्क थियो багтрекер मा स्ट्रिङ तुलना को कार्यान्वयन को छलफल संग glibc। त्यो छलफलबाट थाहा पाउन सकिन्छ glibc для сравнения строк используется आईएसओшная таблица साझा टेम्प्लेट तालिका (CTT), адрес которой можно найти в приложении A стандарта आईएसओ 14651। 2000 र 2015 को बीचमा यो तालिका glibc मर्मतकर्ता थिएन र मानकको हालको संस्करणबाट एकदम फरक थियो (कम्तीमा बाहिरी रूपमा)। 2015 देखि 2018 सम्म, तालिकाको नयाँ संस्करणमा अनुकूलन भयो, र अब तपाइँसँग वास्तविक जीवनमा तालिकाको नयाँ संस्करण भेट्ने मौका छ (CentOS 8), र पुरानो (CentOS 7).

अब हामीसँग एल्गोरिथ्म र सहायक तालिकाहरूको बारेमा सबै जानकारी छ, हामी मूल समस्यामा फर्कन सक्छौं र रूसी लोकेलमा स्ट्रिङहरू कसरी सही रूपमा क्रमबद्ध गर्ने भनेर बुझ्न सक्छौं।

आईएसओ 14651 / 14652

हामीले रुचि राखेको तालिकाको स्रोत कोड CTT धेरै वितरण मा लिनक्स सूचीमा छ /usr/share/i18n/locales/. Сама таблица находится в файле iso14651_t1_common। त्यसपछि यो फाइल निर्देशन हो प्रतिलिपि iso14651_t1_common включается в файл iso14651_t1, जुन, बारीमा, राष्ट्रिय फाइलहरूमा समावेश छ, सहित en_ मा и रु_आरयू। अधिकांश वितरणमा लिनक्स सबै स्रोत फाइलहरू आधारभूत स्थापनामा समावेश छन्, तर यदि तिनीहरू उपस्थित छैनन् भने, तपाईंले वितरणबाट थप प्याकेज स्थापना गर्नुपर्नेछ।

फाइल संरचना iso14651_t1 может показаться ужасно многословной, с неочевидными правилами построения имён, но если разобраться, то всё достаточно просто. Структура описана в стандарте आईएसओ 14652, जसको प्रतिलिपि वेबसाइटबाट डाउनलोड गर्न सकिन्छ open-std.org। फाइल ढाँचाको अर्को विवरण पढ्न सकिन्छ विनिर्देशहरू POSIX देखि खुला समूह। मानक पढ्नको लागि विकल्पको रूपमा, तपाइँ प्रकार्यको स्रोत कोड अध्ययन गर्न सक्नुहुन्छ collate_read в glibc/locale/programs/ld-collate.c.

फाइल संरचना यस्तो देखिन्छ:

По умолчанию, символ используется как экранирующий символ, а конец строки после символа # является комментарием. Оба символа можно переопределить, что и сделано в новой версии таблицы:

escape_char /
comment_char %

В файле будут встречаться токены в формате <Uxxxx> वा <Uxxxxxxxx> (कहाँ x - हेक्साडेसिमल अंक)। यो एन्कोडिङमा युनिकोड कोड बिन्दुहरूको हेक्साडेसिमल प्रतिनिधित्व हो UCS-4 (UTF-32). Все остальные элементы в угловых скобках (в том числе <Uxxxx_xxxx>, <2> र जस्तै) लाई साधारण स्ट्रिङ स्थिरांक मानिन्छ जसको सन्दर्भ बाहिर थोरै अर्थ हुन्छ।

लाइन LC_COLLATE говорит нам, что далее начинаются данные, описывающие сравнение строк.

Сначала задаются имена для весов в таблице сравнения и имена для комбинаций символов. Вообще говоря, два типа имён принадлежат двум разным сущностям, но в реальном файле они перемешаны. Имена весов задаются ключевым словом collating-symbol (символ сравнения), поскольку при сравнении символы Юникода, имеющие одинаковые веса, будут считаться эквивалентными символами.

Общая длина секции в текущей ревизии файла около 900 строк. Я надёргал примеры из нескольких мест, чтобы показать произвольность имён и несколько видов синтаксиса.

LC_COLLATE

collating-symbol <RES-1>
collating-symbol <BLK>
collating-symbol <MIN>
collating-symbol <WIDE>
...
collating-symbol <ARABIC>
collating-symbol <ETHPC>
collating-symbol <OSMANYA>
...
collating-symbol <S1D000>..<S1D35F>
collating-symbol <SFFFF> % Guaranteed largest symbol value. Keep at end of this list
...
collating-element <U0413_0301> from "<U0413><U0301>"
collating-element <U0413_0341> from "<U0413><U0341>"

  • collating - प्रतीक регистрирует строку ओस्मानिया в таблице имён весов
  • collating - प्रतीक .. उपसर्ग समावेश गरी नामहरूको अनुक्रम दर्ता गर्दछ S र हेक्साडेसिमल संख्यात्मक प्रत्यय बाट 1D000 गर्न 1D35F.
  • Ffff в collating - प्रतीक हेक्साडेसिमलमा ठूलो अहस्ताक्षरित पूर्णांक जस्तो देखिन्छ, तर <SFFFF> это просто имя, которое могло бы выглядеть как
  • Имя означает кодовую точку в кодировке UCS-4
  • कोलेटिंग-तत्व बाट " " регистрирует новое имя для пары юникодовских точек.

एकपटक तौलहरूको नाम परिभाषित भएपछि, वास्तविक तौलहरू निर्दिष्ट हुन्छन्। तुलनामा थोरै भन्दा बढी सम्बन्धहरू मात्र महत्त्वपूर्ण हुने हुनाले, तौलहरू सूची नामहरूको सरल अनुक्रमद्वारा निर्धारण गरिन्छ। "हल्का" वजनहरू पहिले सूचीबद्ध छन्, त्यसपछि "भारी"। मलाई तपाईलाई सम्झाउन दिनुहोस् कि प्रत्येक युनिकोड क्यारेक्टरलाई चार फरक वजन तोकिएको छ। यहाँ तिनीहरू एकल क्रमबद्ध अनुक्रममा जोडिएका छन्। सिद्धान्तमा, कुनै पनि प्रतीकात्मक नाम चार स्तरहरूमा प्रयोग गर्न सकिन्छ, तर टिप्पणीहरूले संकेत गर्दछ कि विकासकर्ताहरूले मानसिक रूपमा स्तरहरूमा नामहरू अलग गर्छन्।

% Symbolic weight assignments

% Third-level weight assignments
<RES-1>
<BLK>
<MIN>
<WIDE>
...
% Second-level weight assignments
<BASE>
<LOWLINE> % COMBINING LOW LINE
<PSILI> % COMBINING COMMA ABOVE
<DASIA> % COMBINING REVERSED COMMA ABOVE
...
% First-level weight assignments
<S0009> % HORIZONTAL TABULATION 
<S000A> % LINE FEED
<S000B> % VERTICAL TABULATION
...
<S0434> % CYRILLIC SMALL LETTER DE
<S0501> % CYRILLIC SMALL LETTER KOMI DE
<S0452> % CYRILLIC SMALL LETTER DJE
<S0503> % CYRILLIC SMALL LETTER KOMI DJE
<S0453> % CYRILLIC SMALL LETTER GJE
<S0499> % CYRILLIC SMALL LETTER ZE WITH DESCENDER
<S0435> % CYRILLIC SMALL LETTER IE
<S04D7> % CYRILLIC SMALL LETTER IE WITH BREVE
<S0454> % CYRILLIC SMALL LETTER UKRAINIAN IE
<S0436> % CYRILLIC SMALL LETTER ZHE

Наконец, собственно таблица весов.

Секция весов заключена в строки с ключевыми словами अर्डर_स्टार्ट и order_end. Дополнительные параметры अर्डर_स्टार्ट तुलनाको प्रत्येक स्तरमा कुन दिशामा पङ्क्तिहरू स्क्यान गरिएको छ भनी निर्धारण गर्नुहोस्। पूर्वनिर्धारित सेटिङ छ अगाडि। खण्डको मुख्य भागमा प्रतीक कोड र यसको चार तौलहरू समावेश गर्ने रेखाहरू हुन्छन्। क्यारेक्टर कोड क्यारेक्टर आफै, कोड बिन्दु, वा पहिले परिभाषित प्रतीकात्मक नाम द्वारा प्रतिनिधित्व गर्न सकिन्छ। सांकेतिक नामहरू, कोड बिन्दुहरू, वा प्रतीकहरूलाई पनि तौल दिन सकिन्छ। यदि कोड पोइन्ट वा क्यारेक्टरहरू प्रयोग गरिन्छ भने, तिनीहरूको वजन कोड पोइन्टको संख्यात्मक मान (युनिकोड तालिकामा स्थिति) जस्तै हुन्छ। स्पष्ट रूपमा निर्दिष्ट नगरिएका क्यारेक्टरहरू (मैले बुझेको छु) युनिकोड तालिकाको स्थितिसँग मेल खाने प्राथमिक वजनको साथ तालिकामा तोकिएको मानिन्छ। विशेष वजन मूल्य IGNORE यसको मतलब तुलनाको उपयुक्त स्तरमा प्रतीकलाई बेवास्ता गरिएको छ।

तराजूको संरचना प्रदर्शन गर्न, मैले तीनवटा स्पष्ट टुक्राहरू रोजें:

  • पूर्ण रूपमा बेवास्ता गरिएका पात्रहरू
  • पहिलो दुई तहमा नम्बर तीन बराबर प्रतीकहरू
  • सिरिलिक वर्णमालाको शुरुवात, जसमा diacritics समावेश छैन, र त्यसैले मुख्य रूपमा पहिलो र तेस्रो स्तरहरू द्वारा क्रमबद्ध गरिएको छ।

order_start forward;forward;forward;forward,position
<U0000> IGNORE;IGNORE;IGNORE;IGNORE % NULL (in 6429)
<U0001> IGNORE;IGNORE;IGNORE;IGNORE % START OF HEADING (in 6429)
<U0002> IGNORE;IGNORE;IGNORE;IGNORE % START OF TEXT (in 6429)
...
<U0033> <S0033>;<BASE>;<MIN>;<U0033> % DIGIT THREE
<UFF13> <S0033>;<BASE>;<WIDE>;<UFF13> % FULLWIDTH DIGIT THREE
<U2476> <S0033>;<BASE>;<COMPAT>;<U2476> % PARENTHESIZED DIGIT THREE
<U248A> <S0033>;<BASE>;<COMPAT>;<U248A> % DIGIT THREE FULL STOP
<U1D7D1> <S0033>;<BASE>;<FONT>;<U1D7D1> % MATHEMATICAL BOLD DIGIT THREE
...
<U0430> <S0430>;<BASE>;<MIN>;<U0430> % CYRILLIC SMALL LETTER A
<U0410> <S0430>;<BASE>;<CAP>;<U0410> % CYRILLIC CAPITAL LETTER A
<U04D1> <S04D1>;<BASE>;<MIN>;<U04D1> % CYRILLIC SMALL LETTER A WITH BREVE
<U0430_0306> <S04D1>;<BASE>;<MIN>;<U04D1> % CYRILLIC SMALL LETTER A WITH BREVE
...
<U0431> <S0431>;<BASE>;<MIN>;<U0431> % CYRILLIC SMALL LETTER BE
<U0411> <S0431>;<BASE>;<CAP>;<U0411> % CYRILLIC CAPITAL LETTER BE
<U0432> <S0432>;<BASE>;<MIN>;<U0432> % CYRILLIC SMALL LETTER VE
<U0412> <S0432>;<BASE>;<CAP>;<U0412> % CYRILLIC CAPITAL LETTER VE
...
order_end

Теперь можно снова вернуться к сортировке примеров из начала статьи. Засада кроется вот в этой части таблицы весов:

<U0020> IGNORE;IGNORE;IGNORE;<U0020> % SPACE
<U0021> IGNORE;IGNORE;IGNORE;<U0021> % EXCLAMATION MARK
<U0022> IGNORE;IGNORE;IGNORE;<U0022> % QUOTATION MARK
...

यो तालिकामा विराम चिन्हहरू तालिकाबाट देख्न सकिन्छ ASCII (включая пробел) при сравнении строк практически всегда игнорируется. Исключение составляют лишь строки, совпадающие во всём, кроме знаков препинания, встречающихся в совпадающих позициях. Строки из моего примера (после сортировки) для алгоритма сравнения выглядят так:

АбакановМихаилмаляр
ЁлкинаЭллакрановщица
ИвановаАлламаляр
ИвановАндрейслесарь

तराजूको तालिकामा, रूसीमा क्यापिटल अक्षरहरू सानो अक्षरहरू पछि आउँछन् (तेस्रो तहमा भन्दा भारी <MIN>), क्रमबद्ध एकदम सही देखिन्छ।

चर सेट गर्दा LC_COLLATE=C एक विशेष तालिका लोड गरिएको छ जसले बाइट-द्वारा-बाइट तुलना निर्दिष्ट गर्दछ

static const uint32_t collseqwc[] =
{
  8, 1, 8, 0x0, 0xff,
  /* 1st-level table */
  6 * sizeof (uint32_t),
  /* 2nd-level table */
  7 * sizeof (uint32_t),
  /* 3rd-level table */
  L'x00', L'x01', L'x02', L'x03', L'x04', L'x05', L'x06', L'x07',
  L'x08', L'x09', L'x0a', L'x0b', L'x0c', L'x0d', L'x0e', L'x0f',

...
  L'xf8', L'xf9', L'xfa', L'xfb', L'xfc', L'xfd', L'xfe', L'xff'
};

युनिकोडमा कोड पोइन्ट Ё A भन्दा अगाडि आउँछ, स्ट्रिङहरू तदनुसार क्रमबद्ध हुन्छन्।

पाठ र बाइनरी तालिकाहरू

Очевидно, что сравнение строк, это чрезвычайно часто встречающаяся операция, а разбор таблицы CTT धेरै महँगो प्रक्रिया। तालिकामा पहुँच अप्टिमाइज गर्न, यो आदेशको साथ बाइनरी फारममा कम्पाइल गरिएको छ localdef.

टोली localdef राष्ट्रिय विशेषताहरूको तालिका भएको फाइललाई प्यारामिटरहरूको रूपमा स्वीकार गर्दछ (विकल्प -i), जसमा सबै क्यारेक्टरहरू युनिकोड डटहरूद्वारा प्रतिनिधित्व गरिन्छ, र युनिकोड डटहरू र विशिष्ट सङ्केतनका क्यारेक्टरहरू बीचको पत्राचारको फाइल (विकल्प -f)। कामको नतिजाको रूपमा, बाइनरी फाइलहरू अन्तिम प्यारामिटरमा निर्दिष्ट नामको साथ लोकेलको लागि सिर्जना गरिन्छ।

glibc दुई बाइनरी फाइल ढाँचाहरूलाई समर्थन गर्दछ: "परम्परागत" र "आधुनिक"।

Традиционный формат подразумевает, что имя локали, это имя подкаталога в /usr/lib/locale/। यो उपनिर्देशिकाले बाइनरी फाइलहरू भण्डार गर्छ LC_COLLATE, LC_CTYPE, LC_TIME र यस्तै। फाइल LC_IDENTIFICATION लोकेलको औपचारिक नाम (जुन डाइरेक्टरी नाम भन्दा फरक हुन सक्छ) र टिप्पणीहरू समावेश गर्दछ।

Современный формат предполагает хранение всех локалей в едином архиве /usr/lib/locale/locale-archive, जुन प्रयोग गर्ने सबै प्रक्रियाहरूको भर्चुअल मेमोरीमा म्याप गरिएको छ glibc. Имя локали в современном формате подвергается некоторой канонизации — в названия кодировки остаются только цифры и буквы, приведённые к нижнему регистру. Так ru_RU.KOI8-R, को रूपमा बचत हुनेछ ru_RU.koi8r.

Входные файлы ищутся в текущем каталоге, а так же в каталогах /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ फाइलहरूको लागि CTT र क्रमशः एन्कोडिङ फाइलहरू।

उदाहरणका लागि, आदेश

localedef -i ru_RU -f MAC-CYRILLIC ru_RU.MAC-CYRILLIC

फाइल कम्पाइल गर्नेछ /usr/share/i18n/locales/ru_RU एन्कोडिङ फाइल प्रयोग गर्दै /usr/share/i18n/charmaps/MAC-CYRILLIC.gz र परिणाम बचत गर्नुहोस् /usr/lib/locale/locale-archive नाम अन्तर्गत ru_RU.maccyrillic

यदि तपाईंले चर सेट गर्नुभयो भने LANG = en_US.UTF-8 त्यसपछि glibc будет искать двоичные файлы локали в следующей последовательности файлов и каталогов:

/usr/lib/locale/locale-archive
/usr/lib/locale/en_US.UTF-8/
/usr/lib/locale/en_US/
/usr/lib/locale/enUTF-8/
/usr/lib/locale/en/

Если локаль встречается и в традиционном и в современном форматах, то приоритет отдаётся современному.

तपाईले कमाण्डको साथ संकलित लोकेलहरूको सूची हेर्न सक्नुहुन्छ लोकेल -.

Подготовка своей таблицы сравнения

Теперь, вооружившись знаниями, можно создать собственную идеальную таблицу сравнения строк. Эта таблица должна корректно сравнивать русские буквы, включая букву Ё, и при этом учитывать знаки препинания в соответствии с таблицей ASCII.

Процесс подготовки своей таблицы сортировки состоит из двух этапов: редактирования таблицы весов и компиляции её в двоичную форму командой localdef.

ढाँचामा, न्यूनतम सम्पादन लागतहरू समायोजन गर्नको लागि तुलना तालिकाको लागि आईएसओ 14652 अवस्थित तालिकाको तौल समायोजनका लागि खण्डहरू प्रदान गरिएका छन्। खण्ड एक किवर्ड संग सुरु हुन्छ पुन: क्रमबद्ध र प्रतिस्थापन प्रदर्शन गरिएको स्थिति संकेत गर्दै। खण्ड रेखा संग समाप्त हुन्छ पुन: क्रमबद्ध अन्त्य। यदि यो तालिकाको धेरै खण्डहरू सच्याउन आवश्यक छ भने, त्यस्ता प्रत्येक खण्डको लागि एउटा खण्ड सिर्जना गरिएको छ।

मैले फाइलहरूको नयाँ संस्करणहरू प्रतिलिपि गरें iso14651_t1_common и रु_आरयू भण्डारबाट glibc मेरो गृह निर्देशिकामा ~/.local/share/i18n/locales/ र थोरै खण्ड सम्पादन गरियो LC_COLLATE в रु_आरयू. Новые версии файлов полностью совместимы с моей версией glibc। यदि तपाईं फाइलहरूको पुरानो संस्करणहरू प्रयोग गर्न चाहनुहुन्छ भने, तपाईंले प्रतीकात्मक नामहरू र तालिकामा प्रतिस्थापन सुरु हुने ठाउँ परिवर्तन गर्नुपर्नेछ।

LC_COLLATE
% Copy the template from ISO/IEC 14651
copy "iso14651_t1"
reorder-after <U000D>
<U0020> <S0020>;<BASE>;<MIN>;<U0020> % SPACE
<U0021> <S0021>;<BASE>;<MIN>;<U0021> % EXCLAMATION MARK
<U0022> <S0022>;<BASE>;<MIN>;<U0022> % QUOTATION MARK
...
<U007D> <S007D>;<BASE>;<MIN>;<U007D> % RIGHT CURLY BRACKET
<U007E> <S007E>;<BASE>;<MIN>;<U007E> % TILDE
reorder-end
END LC_COLLATE

वास्तवमा, यो मा क्षेत्रहरू परिवर्तन गर्न आवश्यक हुनेछ LC_IDENTIFICATION ताकि तिनीहरूले लोकेललाई औंल्याए ru_MY, но в моём примере это не потребовалось, поскольку я исключил из поиска локалей архив locale-archive.

कि localdef работала с файлами в моей папке через переменную I18NPATH तपाईंले इनपुट फाइलहरू खोज्नको लागि अतिरिक्त डाइरेक्टरी थप्न सक्नुहुन्छ, र बाइनरी फाइलहरू बचत गर्नका लागि डाइरेक्टरीलाई स्ल्याशहरू सहितको मार्गको रूपमा निर्दिष्ट गर्न सकिन्छ:

$> I18NPATH=~/.local/share/i18n localedef -i ru_RU -f UTF-8 ~/.local/lib/locale/ru_MY.UTF-8

POSIX मा मान्छ बस तपाईले लोकेल फाइलहरूसँग डाइरेक्टरीहरूमा निरपेक्ष पथहरू लेख्न सक्नुहुन्छ, फर्वार्ड स्ल्याशबाट सुरू गर्दै, तर glibc в लिनक्स सबै पथहरू आधार डाइरेक्टरीबाट गणना गरिन्छ, जुन चर मार्फत ओभरराइड गर्न सकिन्छ LOCPATH। स्थापना पछि LOCPATH=~/.local/lib/locale/ स्थानीयकरणसँग सम्बन्धित सबै फाइलहरू मेरो फोल्डरमा मात्र खोजिनेछन्। चर सेट संग लोकेल को अभिलेख LOCPATH बेवास्ता गरियो।

Вот он решающий тест:

$> LANG=ru_MY.UTF-8 LOCPATH=~/.local/lib/locale/ sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Иванова Алла;адвокат

हुर्रे! हामीले गर्यो!

बग कार्य

मैले सुरुमा राखिएको स्ट्रिङ क्रमबद्ध गर्ने बारे प्रश्नहरूको जवाफ दिइसकेको छु, तर त्रुटिहरूको बारेमा अझै केही प्रश्नहरू छन् - दृश्य र अदृश्य।

मूल समस्यामा फर्कौं।

र कार्यक्रम भाग्य र कार्यक्रम सामेल используют одни и те же функции сравнения строк из glibc. Как же получилось, что सामेल आदेशद्वारा क्रमबद्ध पङ्क्तिहरूमा क्रमबद्ध त्रुटि दिनुभयो भाग्य लोकेलमा en_US.UTF-8? जवाफ सरल छ: भाग्य सम्पूर्ण स्ट्रिङ तुलना गर्दछ, र सामेल сравнивает только ключ, которым по умолчанию, является начало строки до первого пробельного символа. В моём примере это привело к сообщению об ошибке поскольку сортировка первых слов в строках не совпала с сортировкой полных строк.

लोकेल "C" ग्यारेन्टी गर्दछ कि क्रमबद्ध स्ट्रिङहरूमा पहिलो स्पेस सम्मको प्रारम्भिक सबस्ट्रिङहरू पनि क्रमबद्ध हुनेछन्, तर यसले त्रुटिलाई मात्र मास्क गर्छ। त्रुटि सन्देश बिना, गलत फाइल मर्ज परिणाम दिने डेटा चयन गर्न सम्भव छ (एउटै थर भएका व्यक्तिहरू, तर फरक पहिलो नामहरू)। यदि हामी चाहन्छौं भने सामेल पूरा नामद्वारा फाइल लाइनहरू मर्ज गर्नुहोस्, त्यसपछि सही तरिका स्पष्ट रूपमा फिल्ड सेपरेटर निर्दिष्ट गर्ने र कुञ्जी फिल्डद्वारा क्रमबद्ध गर्ने हो, र सम्पूर्ण लाइनद्वारा होइन। यस अवस्थामा, मर्ज सही रूपमा अगाडि बढ्नेछ र त्यहाँ कुनै पनि लोकेलमा कुनै त्रुटि हुनेछैन:

$> sort -t ; -k 1 buhg.txt > buhg.srt
$> sort -t ; -k 1 mail.txt > mail.srt
$> join -t ; buhg.srt mail.srt > result

इन्कोडिङमा उदाहरण सफलतापूर्वक कार्यान्वयन गरियो CP1251 содержит ещё одну ошибку. Дело в том, что во всех известных мне дистрибутивах लिनक्स प्याकेजहरू कम्पाइल गरिएको लोकेल हराइरहेका छन् ru_RU.CP1251। यदि कम्पाइल गरिएको लोकेल फेला परेन भने, त्यसपछि भाग्य चुपचाप बाइट-द्वारा-बाइट तुलना प्रयोग गर्दछ, जुन हामीले अवलोकन गर्यौं।

Кстати, есть ещё один маленький глюк связанный с недоступностью скомпилированных локалей. Команда LOCPATH=/tmp लोकेल -a выдаст список всех локалей в locale-archive, तर चर सेट संग LOCPATH सबै कार्यक्रमहरूको लागि (अधिकांश सहित स्थानीय) यी स्थानहरू उपलब्ध हुने छैनन्।

$> LOCPATH=/tmp locale -a | grep en_US
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_COLLATE to default locale: No such file or directory
en_US
en_US.iso88591
en_US.iso885915
en_US.utf8

$> LC_COLLATE=en_US.UTF-8 sort --debug
sort: using ‘en_US.UTF-8’ sorting rules

$> LOCPATH=/tmp LC_COLLATE=en_US.UTF-8 sort --debug
sort: using simple byte comparison

निष्कर्षमा

यदि तपाइँ एक प्रोग्रामर हुनुहुन्छ जो सोच्न प्रयोग गरिन्छ कि स्ट्रिङ बाइट को एक सेट हो, त्यसपछि तपाइँको छनोट LC_COLLATE=C.

Если вы лингвист или составитель словарей, то вам лучше скомпилировать свою локаль.

यदि तपाइँ एक साधारण प्रयोगकर्ता हुनुहुन्छ भने, तपाइँ केवल आदेश को तथ्य मा प्रयोग गर्न आवश्यक छ ls -a выдаёт файлы, начинающиеся с точки, вперемешку с файлами, начинающимися с буквы, а मध्यरातको कमाण्डर, जसले नामहरू क्रमबद्ध गर्न यसको आन्तरिक प्रकार्यहरू प्रयोग गर्दछ, सूचीको सुरुमा डटबाट सुरु हुने फाइलहरू राख्छ।

सन्दर्भ

Отчёт №10 Unicode collation algorithm

Веса символов на unicode.org

आईसीयू - IBM बाट युनिकोड संग काम गर्न को लागी एक पुस्तकालय को कार्यान्वयन।

प्रयोग गरी क्रमबद्ध परीक्षण आईसीयू

Веса символов в आईएसओ 14651

स्केलको साथ फाइल ढाँचाको विवरण आईएसओ 14652

मा स्ट्रिङ तुलना को छलफल glibc

स्रोत: www.habr.com

एक टिप्पणी थप्न