Linux-un sətirləri necə çeşidləməsi

Giriş

Hamısı ünvanlar haqqında məlumatları birləşdirməli olan qısa bir skriptlə başladı e-mail kadrlar bazasından alınan işçi mövqeləri ilə poçt siyahısı istifadəçilərinin siyahısından alınan işçilər. Hər iki siyahı Unicode mətn fayllarına ixrac edildi. UTF-8 və Unix xətt sonluğu ilə saxlanılır.

Məzmun mail.txt

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

Məzmun buhg.txt

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

Birləşdirmək üçün fayllar Unix əmri ilə çeşidləndi cür və Unix proqramının girişinə təqdim olunur qoşulmaq, gözlənilmədən xəta ilə bitdi:

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

Çeşidləmə nəticəsinə gözlərlə baxmaq göstərdi ki, ümumilikdə çeşidləmə düzgündür, lakin kişi və qadın soyadlarının üst-üstə düşməsi zamanı qadın soyadları kişilərdən əvvəl gedir:

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

Unicode-da çeşidləmə xətası və ya çeşidləmə alqoritmində feminizmin təzahürü kimi görünür. Birincisi, əlbəttə ki, daha inandırıcıdır.

Hələlik bunu təxirə salaq qoşulmaq və diqqət yetirin cür. Problemi elmi poke üsulu ilə həll etməyə çalışaq. Əvvəlcə yerli dili dəyişdirin az haqqında ru_RU. Çeşidləmə üçün mühit dəyişənini təyin etmək kifayətdir LC_COLLATE, amma xırda şeylərə vaxt itirməyəcəyik:

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

Heçnə dəyişmədi.

Faylları bir baytlıq kodlaşdırmaya yenidən kodlaşdırmağa çalışaq:

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

Yenə də heç nə dəyişməyib.

Heç bir şey etmək mümkün deyil, İnternetdə bir həll axtarmaq lazımdır. Rus soyadları haqqında birbaşa heç bir şey yoxdur, lakin digər çeşidləmə qəribəlikləri ilə bağlı suallar var. Məsələn, burada bir problem var: unix sort '-' (tire) simvollarını görünməz kimi qəbul edir. Bir sözlə, "ab", "aa", "ac" sətirləri "aa", "ab", "ac" kimi sıralanır.

Cavab hər yerdə standartdır: proqramçının dilini istifadə edin "C" və xoşbəxt olacaqsınız. Çalışırıq:

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

Nəsə dəyişdi. Yolkina harasa sürüşsə də, İvanovlar düzgün ardıcıllıqla düzüldülər. Orijinal problemə qayıdaq:

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

İnternetin vəd etdiyi kimi, səhvsiz işləyirdi. Və buna baxmayaraq Yolkina birinci sırada.

Problem həll edilmiş kimi görünür, amma hər halda, başqa bir rus kodlamasını - Windows-u sınayaq CP1251:

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

Sıralama nəticəsi, qəribə də olsa, yerli dilə uyğun olacaq "C", və bütün nümunə, müvafiq olaraq, səhvsiz keçir. Bir növ mistik.

Proqramlaşdırmada mistisizmi sevmirəm, çünki o, adətən səhvləri gizlədir. Bunun necə işlədiyinə ciddi yanaşmalıyıq. cür və nə təsir edir LC_COLLATE .

Sonda suallara cavab verməyə çalışacağam:

  • qadınların soyadları niyə səhv sıralanıb
  • niyə LANG=ru_RU.CP1251 ekvivalent olduğu ortaya çıxdı LANG=C
  • niyə sən cür и qoşulmaq çeşidlənmiş sətirlərin sırası haqqında müxtəlif fikirlər
  • niyə bütün nümunələrimdə səhvlər var
  • nəhayət simləri öz zövqünüzə görə necə çeşidləmək olar

Unicode ilə çeşidləmə

İlk dayanacaq №10 texniki hesabat olacaq Unicode harmanlama alqoritmi online unicode.org. Hesabatda çoxlu texniki detallar var, ona görə də mən əsas fikirlərin xülasəsini verməkdən azadam.

müqayisəsi - sətirlərin “müqayisəsi” istənilən çeşidləmə alqoritminin əsasını təşkil edir. Alqoritmlərin özləri fərqli ola bilər ("baloncuk", "birləşmə", "sürətli"), lakin onların hamısı sıralarını müəyyən etmək üçün bir cüt sətirin müqayisəsindən istifadə edəcəklər.

Təbii dildə sətirlərin çeşidlənməsi olduqca çətin məsələdir. Ən sadə tək baytlıq kodlaşdırmalarda belə, əlifbadakı hərflərin sırası, hətta ingilis latın əlifbasından bir qədər fərqli olsa belə, artıq bu hərflərin kodlandığı ədədi dəyərlərin sırası ilə üst-üstə düşməyəcəkdir. Beləliklə, alman əlifbasında hərf Ö arasında dayanır О и P, və kodlaşdırmada CP850 arasına düşür ÿ и Ü.

Xüsusi kodlaşdırmadan mücərrədləşməyə cəhd edə və Unicode-da olduğu kimi müəyyən qaydada düzülmüş "ideal" hərfləri nəzərdən keçirə bilərsiniz. Kodlaşdırmalar UTF8, UTF16 və ya tək bayt KOI8-R (məhdud Unicode alt dəstinə ehtiyac varsa) hərflərin müxtəlif ədədi təsvirlərini verəcək, lakin eyni əsas cədvəl elementlərinə istinad edəcək.

Belə çıxır ki, simvol cədvəlini sıfırdan qursaq belə, ona universal simvol sırası təyin edə bilmərik. Eyni hərflərdən istifadə edən müxtəlif milli əlifbalarda bu hərflərin sırası fərqli ola bilər. Məsələn, fransız dilində Æ liqatura kimi qəbul ediləcək və sətir kimi çeşidlənəcək AE. Norveç dilində Æ sonra yerləşən ayrı bir məktub olacaq Z. Yeri gəlmişkən, kimi ligaturlardan başqa Æ bir neçə simvolla yazılmış hərflər var. Beləliklə, çex əlifbasında hərf var Charasında dayanır H и I.

Əlifba fərqindən əlavə, çeşidlənməyə təsir edən başqa milli ənənələr də var. Xüsusilə sual yaranır: böyük və kiçik hərflərdən ibarət sözlər lüğətdə hansı ardıcıllıqla yer almalıdır? Həmçinin, çeşidləmə durğu işarələrindən istifadənin xüsusiyyətlərindən təsirlənə bilər. İspan dilində sual cümləsinin əvvəlinə tərs sual işarəsi qoyulur (Musiqi xoşunuza gəlirmi?). Belə olan halda aydındır ki, sorğu cümlələri əlifbadan kənarda ayrıca klasterdə qruplaşdırılmamalı, fərqli durğu işarələri olan sətirləri necə çeşidləmək olar?

Avropa dillərindən çox fərqli dillərdə sətirlərin çeşidlənməsi üzərində dayanmayacağam. Qeyd edək ki, sağdan sola və ya yuxarıdan aşağıya doğru olan dillər simvolları oxu qaydasında sətirlərdə saxlamağa meyllidirlər və hətta qeyri-əlifbalı skriptlərin sətirləri simvoldan simvol sıralamaq üçün öz üsulları var. Məsələn, heroqliflər üsluba görə sıralana bilər (Çin simvol düymələri) və ya tələffüzlə. Emoji necə sifariş edilməlidir, düzünü desəm, heç bir fikrim yoxdur, amma onlar üçün bir şey düşünə bilərsiniz.

Yuxarıda sadalanan xüsusiyyətlərə əsasən, Unicode cədvəlləri əsasında sətirlərin müqayisəsi üçün əsas tələblər tərtib edilmişdir:

  • sətirlərin müqayisəsi kod cədvəlindəki simvolların mövqeyindən asılı deyil;
  • tək simvol təşkil edən simvol ardıcıllığı kanonik formaya endirilir (A + yuxarı dairə eynidir Å);
  • sətirləri müqayisə edərkən simvol bir sətir kontekstində nəzərdən keçirilir və lazım olduqda qonşularla bir müqayisə vahidinə birləşdirilir (Ch çex dilində) və ya bir neçə hissəyə (Æ fransız dilində);
  • bütün milli xüsusiyyətlər (əlifba, böyük/kiçik hərf, durğu işarələri, yazı növlərinin sırası) sifarişin (emoji) əl ilə təyin edilməsinə qədər konfiqurasiya edilməlidir;
  • Müqayisə təkcə çeşidləmə üçün deyil, həm də bir çox başqa yerlərdə, məsələn, sətirlərin diapazonunu ({A ... z} ilə əvəz etməklə) müəyyən etmək üçün vacibdir. bash);
  • müqayisə kifayət qədər sürətli olmalıdır.

Bundan əlavə, hesabatın müəllifləri alqoritm tərtibatçılarının etibar etməməli olduğu müqayisə xüsusiyyətlərini tərtib etdilər:

  • müqayisə alqoritmi hər dil üçün ayrıca simvol dəsti tələb etməməlidir (rus və ukrayna dillərində ən çox kiril hərfləri var);
  • müqayisə Unicode cədvəllərində simvolların sırasına əsaslanmamalıdır;
  • sətirin çəkisi sətirin atributu olmamalıdır, çünki müxtəlif mədəni kontekstlərdə eyni sim müxtəlif çəkilərə malik ola bilər;
  • cərgə çəkiləri birləşdirildikdə və ya bölündükdə dəyişə bilər (dan x < y buna əməl etmir xz < yz);
  • eyni çəkiyə malik olan müxtəlif sətirlər çeşidləmə alqoritmi baxımından bərabər hesab edilir. Belə sətirlərin əlavə sıralanmasının tətbiqi mümkündür, lakin bu, performansı aşağı sala bilər;
  • eyni çəkilərə malik cərgələr təkrar çeşidləmə zamanı dəyişdirilə bilər. Davamlılıq sətirlərin müqayisəsi alqoritminin xassəsi deyil, müəyyən çeşidləmə alqoritminin xassəsidir (əvvəlki bəndə bax);
  • mədəni təcrübələr təkmilləşdikcə/dəyişdikcə çeşidləmə qaydaları zamanla dəyişə bilər.

Müqayisə alqoritminin işlənmiş sətirlərin semantikası haqqında heç nə bilməməsi də şərtlənir. Beləliklə, yalnız rəqəmlərdən ibarət olan sətirlər rəqəmlərlə müqayisə edilməməli və ingilis adları siyahılarında məqalə çıxarılmamalıdır (The Beatles).

Bütün bu tələblərə cavab vermək üçün çoxsəviyyəli (əslində dördsəviyyəli) cədvəlli çeşidləmə alqoritmi təklif olunur.

Əvvəllər sətirdəki simvollar kanonik formaya çevrilir və müqayisə vahidlərinə qruplaşdırılır. Hər bir müqayisə vahidinə bir neçə müqayisə səviyyəsinə uyğun gələn bir neçə çəki verilir. Müqayisə vahidlərinin çəkiləri sıralı çoxluqların elementləridir (bu halda, tam ədədlər) daha çox və ya daha az müqayisə oluna bilər. xüsusi məna NORMAL (0x0) bu vahidin müvafiq müqayisə səviyyəsində müqayisəyə cəlb edilmədiyini bildirir. Sətirlərin müqayisəsi müvafiq səviyyələrin çəkilərindən istifadə etməklə bir neçə dəfə təkrarlana bilər. Hər səviyyədə iki sətirin müqayisə vahidlərinin çəkiləri ardıcıl olaraq bir-biri ilə müqayisə edilir.

Fərqli milli ənənələr üçün alqoritmin müxtəlif tətbiqlərində əmsalların dəyərləri fərqli ola bilər, lakin Unicode standartına əsas çəki cədvəli daxildir - "Defolt Unicode Collation Element Cədvəli" (DUCET). Qeyd etmək istəyirəm ki, dəyişən təyin olunur LC_COLLATE əslində simli müqayisə funksiyasında çəki cədvəlinin seçiminin göstəricisidir.

Çəki əmsalları DUCET aşağıdakı kimi təşkil edilmişdir:

  • birinci səviyyədə bütün hərflər eyni hala endirilir, diakritik işarələr atılır, durğu işarələri (hamısı deyil) nəzərə alınmır;
  • ikinci səviyyədə yalnız diakritiklər nəzərə alınır;
  • üçüncü səviyyə yalnız haldır;
  • dördüncü səviyyədə yalnız durğu işarələri nəzərə alınır.

Müqayisə bir neçə keçiddə baş verir: birincisi, birinci səviyyənin əmsalları müqayisə edilir; çəkilər uyğun gəlirsə, o zaman ikinci səviyyənin çəkiləri ilə yenidən müqayisə edilir; sonra bəlkə üçüncü və dördüncü.

Sətirlərdə müxtəlif çəkilərə malik uyğun müqayisə vahidləri olduqda, müqayisə başa çatır. Bütün dörd səviyyədə bərabər çəkiyə malik olan sıralar bir-birinə bərabər hesab olunur.

Bu alqoritm (bir sıra əlavə texniki detallarla) №10 hesabata ad verdi - Unicode Collation Alqoritmi (ACU).

Burada nümunəmizdəki çeşidləmə davranışı bir az daha aydın olur. Unicode standartı ilə müqayisə etmək yaxşı olardı.

Tətbiqləri sınaqdan keçirmək üçün ACU xüsusi var sınaqistifadə edərək çəki faylı, həyata keçirmək DUCET. Ağırlıqlar faylında müxtəlif əyləncələr tapa bilərsiniz. Məsələn, mahjong və Avropa dominolarının sırası, eləcə də kart göyərtəsində kostyumların sırası var (simvol 1F000 və daha çox). Kart kostyumları körpünün qaydalarına uyğun olaraq yerləşdirilir - PCBT, və kostyumda kartlar - ardıcıllıqla T,2,3 ... K.

uyğun olaraq sətirlərin düzgün çeşidlənməsinin əllə yoxlanılması DUCET olduqca yorucu olardı, amma xoşbəxtlikdən bizim üçün Unicode ilə işləmək üçün istinad kitabxanası tətbiqi var - "Unicode üçün Beynəlxalq Komponentlər"(ICU).

Bu kitabxananın saytında, ildə hazırlanmışdır IBM, o cümlədən demo səhifələr var simli müqayisə alqoritmi səhifəsi. Test sətirlərimizi standart parametrlərlə daxil edirik və bax, mükəmməl rus çeşidlənməsi əldə edirik.

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

Yeri gəlmişkən, veb-sayt ICU durğu işarələrini işləyərkən müqayisə alqoritminin dəqiqləşdirilməsini tapa bilərsiniz. Nümunələrdə Collation FAQ apostrof və tire nəzərə alınmır.

Unicode bizə kömək etdi, lakin qəribə davranışın səbəblərini axtarın cür в Linux başqa yerə getməli olacaq.

Glibc-də çeşidləmə

Kommunal mənbə kodlarının sürətli görünüşü cür haqqında GNU Core Utils kommunalın özündə lokalizasiyanın dəyişənin cari dəyərini çap etmək üçün azaldığını göstərdi LC_COLLATE debug rejimində işləyərkən:

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

Sətirlərin müqayisəsi standart funksiya ilə həyata keçirilir strcoll, bu o deməkdir ki, maraqlı hər şey kitabxanadadır glibc.

Haqqında wiki Layihə glibc simli müqayisəyə həsr olunmuşdur bir paraqraf. Bu paraqrafdan belə başa düşmək olar glibc çeşidləmə artıq bizə məlum olan alqoritmə əsaslanır ACU (Unicode harmanlama alqoritmi) və/və ya ona yaxın standartda ISO 14651 (Beynəlxalq simli sıralama və müqayisə). Ən son standartla bağlı qeyd etmək lazımdır ki, saytda standards.iso.org ISO 14651 rəsmi olaraq açıq elan edilir, lakin müvafiq keçid mövcud olmayan səhifəyə aparır. Google, standartın elektron surətini yüz avroya almağı təklif edən rəsmi saytlara keçidləri olan bir neçə səhifə verir, lakin axtarış nəticələrinin üçüncü və ya dördüncü səhifəsində birbaşa bağlantılar da var. PDF. Ümumiyyətlə, standart praktiki olaraq fərqlənmir ACU, lakin simli çeşidləmənin milli xüsusiyyətlərinə dair parlaq nümunələr olmadığı üçün daha darıxdırıcı oxunur.

Ən maraqlı məlumat wiki linki var idi səhv izləyicisi simli müqayisənin həyata keçirilməsinin müzakirəsi ilə glibc. Müzakirələrdən də görünür ki glibc sətirlərin müqayisəsi üçün istifadə olunur ISOshnaya masası Ümumi Şablon Cədvəli (CTT), ünvanını ərizədə tapa bilərsiniz A standart ISO 14651. 2000-2015-ci illər arasında bu cədvəldə glibc baxıcı yox idi və standartın hazırkı versiyasından tamamilə fərqli idi (ən azı zahirən). 2015-ci ildən 2018-ci ilə qədər cədvəlin yeni versiyasına uyğunlaşma var idi və bu anda masanın yeni versiyası kimi real həyatda görüşmək şansınız var (CentOS 8) və köhnə (CentOS 7).

İndi alqoritm və köməkçi cədvəllər haqqında bütün məlumatları əldə etdikdən sonra, orijinal problemə qayıda bilərik və rus dilində sətirləri necə düzgün şəkildə çeşidləmək lazım olduğunu başa düşə bilərik.

ISO 14651 / 14652

Bizi maraqlandıran cədvəlin mənbə kodu CTT əksər paylamalarda Linux kataloqdadır /usr/share/i18n/locales/. Cədvəlin özü fayldadır iso14651_t1_common. Sonra bu fayl bir direktivdir iso14651_t1_common kopyalayın fayla daxildir iso14651_t1, bu da öz növbəsində milli fayllara, o cümlədən az и ru_RU. Əksər paylamalarda Linux bütün mənbə faylları əsas quraşdırmaya daxildir, lakin onlar yoxdursa, onda siz paylamadan əlavə paket quraşdırmalı olacaqsınız.

Fayl strukturu iso14651_t1 adların qurulması üçün qeyri-aşkar qaydaları ilə olduqca müfəssəl görünə bilər, amma başa düşsəniz, hər şey olduqca sadədir. Quruluş standartda təsvir edilmişdir ISO 14652, bir nüsxəsini saytdan yükləmək olar open-std.org. Fayl formatının başqa bir təsvirini burada tapa bilərsiniz spesifikasiyalar POSIX etibarən açıq qrup. Standartı oxumağa alternativ olaraq, funksiyanın mənbə mətnlərini öyrənə bilərsiniz harmanla_oxu в glibc/locale/programs/ld-collate.c.

Fayl strukturu belə görünür:

Varsayılan olaraq, simvol qaçış simvolu kimi istifadə olunur və # simvolundan sonra sətrin sonu şərhdir. Hər iki simvol yenidən təyin edilə bilər, bu cədvəlin yeni versiyasında edilir:

escape_char /
comment_char %

Faylda formatda işarələr olacaq və ya (Harada x onaltılıq rəqəmdir). Bu kodlaşdırmada Unicode kod nöqtələrinin onaltılıq təmsilidir UCS-4 (UTF-32). Bucaqlı mötərizədə olan bütün digər elementlər (o cümlədən , <2> və s.) kontekstdən çox məna kəsb etməyən sadə sətir sabitləri hesab olunur.

Sətir LC_COLLATE sətirlərin müqayisələrini təsvir edən verilənlərin növbəti dəfə başlayacağını bildirir.

Əvvəlcə müqayisə cədvəlində çəkilər üçün adlar və simvol birləşmələri üçün adlar verilir. Ümumiyyətlə, iki növ ad iki fərqli quruma aiddir, lakin onlar faktiki faylda qarışdırılır. Ağırlıqların adları açar sözlə verilir yığım simvolu (müqayisə xarakteri) çünki eyni çəkiyə malik Unicode simvolları müqayisə edildikdə ekvivalent simvollar kimi qəbul ediləcək.

Faylın cari reviziyasındakı bölmənin ümumi uzunluğu təxminən 900 sətirdir. Təsadüfi adları və bir neçə növ sintaksisi göstərmək üçün bir neçə yerdən nümunələr çəkdim.

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>"

  • yığım simvolu sətri qeyd edir OSMANYA miqyaslı ad cədvəlində
  • yığım simvolu .. prefiksdən ibarət adlar ardıcıllığını qeyd edir S və onaltılıq rəqəmli şəkilçi 1D000 üzrə 1D35F.
  • Ffff в yığım simvolu hexadecimal böyük işarəsiz tam kimi görünür, lakin bu sadəcə olaraq görünə biləcək bir addır
  • имя kodlaşdırmada kod nöqtəsi deməkdir UCS-4
  • yığma elementi -dən" " bir cüt unicode nöqtəsi üçün yeni ad qeyd edir.

Ağırlıqların adları müəyyən edildikdən sonra faktiki çəkilər müəyyən edilir. Müqayisə üçün yalnız böyük-az nisbətləri əhəmiyyətli olduğundan, çəkilər adların sadalanmasının sadə ardıcıllığı ilə müəyyən edilir. Əvvəlcə "yüngül" çəkilər, sonra isə "ağır" çəkilər qeyd olunur. Xatırladaq ki, hər Unicode simvoluna dörd fərqli çəki təyin olunur. Burada onlar bir sıralı ardıcıllıqla ümumiləşdirilir. Nəzəri olaraq, hər hansı bir simvolik ad dörd səviyyədən hər hansı birində istifadə edilə bilər, lakin şərhlər tərtibatçıların adları zehni olaraq səviyyələrə ayırdığını göstərir.

% 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

Nəhayət, faktiki çəki cədvəli.

Çəki bölməsi açar söz sətirləri içərisindədir sifariş_start и sifariş_sonu. Əlavə seçimlər sifariş_start hər bir müqayisə səviyyəsində sətirlərin hansı istiqamətdə skan edildiyini müəyyən edin. Standart parametrdir irəli. Bölmənin gövdəsi simvol kodunu və onun dörd çəkisini ehtiva edən sətirlərdən ibarətdir. Simvol kodu simvolun özü, kod nöqtəsi və ya əvvəllər müəyyən edilmiş simvolik adla təmsil oluna bilər. Çəkilər simvolik adlara, kod nöqtələrinə və ya simvolların özlərinə də verilə bilər. Əgər kod nöqtələri və ya simvollar istifadə olunursa, onların çəkisi kod nöqtəsinin ədədi dəyəri ilə eynidir (Unicode cədvəlindəki mövqe). Açıq şəkildə olmayan simvollar (mən başa düşdüyüm kimi) Unicode cədvəlindəki mövqeyə uyğun gələn əsas çəki ilə cədvələ təyin edilmiş hesab olunur. Xüsusi çəki dəyəri İGNORE müvafiq müqayisə səviyyəsində verilmiş simvolun nəzərə alınmaması deməkdir.

Çəkilərin quruluşunu nümayiş etdirmək üçün üç aydın fraqment seçdim:

  • tamamilə nəzərə alınmayan simvollar
  • ilk iki səviyyədə üç rəqəmə bərabər olan simvollar
  • diakritikləri ehtiva etməyən və buna görə də əsasən birinci və üçüncü səviyyələrə görə sıralanan kiril əlifbasının başlanğıcı.

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

İndi məqalənin əvvəlindən nümunələrin çeşidlənməsinə qayıda bilərsiniz. Pusu çəki cədvəlinin bu hissəsindədir:

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

Görünür ki, bu cədvəldə durğu işarələri cədvəldən qoyulur ASCII (boşluq daxil olmaqla) sətirləri müqayisə edərkən demək olar ki, həmişə nəzərə alınmır. Yalnız istisnalar, uyğun mövqelərdə baş verən durğu işarələri istisna olmaqla, hər şeydə uyğun gələn sətirlərdir. Müqayisə alqoritmi üçün nümunəmdən (çeşidləndikdən sonra) sətirlər belə görünür:

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

Nəzərə alsaq ki, çəki cədvəlində rus dilində böyük hərflər kiçik hərflərdən sonra gəlir (üçüncü səviyyədə -dən daha çətindir ), çeşidləmə tamamilə düzgün görünür.

Dəyişən təyin edərkən LC_COLLATE=C bayt-bayt müqayisəsini təyin edən xüsusi cədvəl yüklənir

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'
};

Unicode kod nöqtəsi Ё A-dan əvvəl gəldiyi üçün sətirlər müvafiq olaraq sıralanır.

Mətn və ikili cədvəllər

Aydındır ki, sətirlərin müqayisəsi olduqca ümumi əməliyyatdır və cədvəlin təhlilidir CTT olduqca bahalı prosedurdur. Cədvələ girişi optimallaşdırmaq üçün o, komanda ilə binar formada tərtib edilir localdef.

Komanda localdef Milli xüsusiyyətlər cədvəli olan faylı parametrlər kimi qəbul edir (seçim -i), burada bütün simvollar Unicode nöqtələri ilə təmsil olunur və xüsusi kodlaşdırmanın simvolları üçün Unicode nöqtə xəritələşdirmə faylı (seçim) -f). İşin nəticəsi olaraq, son parametrdə göstərilən adla yerli üçün ikili fayllar yaradılır.

glibc iki ikili fayl formatını dəstəkləyir: "ənənəvi" və "müasir".

Ənənəvi format yerli adının bir alt kataloqun adı olduğunu nəzərdə tutur /usr/lib/locale/. Bu alt kataloq ikili faylları ehtiva edir LC_COLLATE, LC_CTYPE, LC_TIME və s. Fayl LC_IDENTIFICATION formal yerli adı (kataloq adından fərqli ola bilər) və şərhləri ehtiva edir.

Müasir format bütün yerlilərin bir arxivdə saxlanmasını nəzərdə tutur /usr/lib/locale/locale-arxiv, istifadə edilən bütün proseslərin virtual yaddaşına uyğunlaşdırılır glibc. Müasir formatda yerli dilin adı bəzi kanonikləşdirməyə məruz qalır - kodlaşdırma adında yalnız kiçik hərflərə endirilmiş rəqəmlər və hərflər qalır. Belə ki en_RU.KOI8-R, olaraq qeyd olunacaq ru_RU.koi8r.

Daxil olan fayllar cari kataloqda, həmçinin kataloqlarda axtarılır /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ fayllar üçün CTT və müvafiq olaraq kodlaşdırma faylları.

Məsələn, əmr

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

faylı tərtib edəcək /usr/share/i18n/locales/ru_RU kodlaşdırma faylından istifadə etməklə /usr/share/i18n/charmaps/MAC-CYRILLIC.gz və nəticəni yaddaşda saxlayın /usr/lib/locale/locale-arxiv adı altında en_RU.maccyrillic

Bir dəyişən təyin etsəniz LANG = en_US.UTF-8 o glibc aşağıdakı fayl və qovluqlar ardıcıllığında yerli ikili faylları axtaracaq:

/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/

Lokal həm ənənəvi, həm də müasir formatda baş verirsə, müasir olan üstünlük təşkil edir.

Komanda ilə tərtib edilmiş yerlilərin siyahısına baxa bilərsiniz yerli-a.

Müqayisə cədvəlinizin hazırlanması

İndi biliklə silahlanmış, öz ideal simli müqayisə cədvəlinizi yarada bilərsiniz. Bu cədvəl rus hərflərini, o cümlədən Ё hərfini düzgün müqayisə etməli və eyni zamanda cədvələ uyğun olaraq durğu işarələrini nəzərə almalıdır. ASCII.

Çeşidləmə cədvəlinizin hazırlanması prosesi iki mərhələdən ibarətdir: çəki cədvəlini redaktə etmək və komanda ilə ikili formada tərtib etmək localdef.

Müqayisə cədvəlinin minimum redaktə xərcləri ilə formatda tənzimlənməsi üçün ISO 14652 mövcud cədvəlin çəkilərini tənzimləmək üçün bölmələr verilir. Bölmə açar sözlə başlayır yenidən sıraladıqdan sonra və dəyişdirmə yerinə yetirildikdən sonra vəzifənin təyin edilməsi. Bölməni xəttlə bitirir yenidən sırala. Cədvəlin bir neçə bölməsini düzəltmək lazımdırsa, onda hər bir belə bölmə üçün bölmə yaradılır.

Faylların yeni versiyalarını kopyaladım iso14651_t1_common и ru_RU anbardan glibc ev kataloqunuza ~/.local/share/i18n/locales/ və bölməni bir az redaktə etdi LC_COLLATE в ru_RU. Faylların yeni versiyaları mənim versiyamla tam uyğundur glibc. Əgər faylların köhnə versiyalarından istifadə etmək istəyirsinizsə, onda siz simvolik adları və cədvəldə dəyişdirmənin başladığı yeri dəyişməli olacaqsınız.

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

Əslində, sahələri dəyişdirmək lazım olacaq LC_IDENTIFICATION belə ki, onlar yerli yeri göstərirlər az_MY, lakin mənim nümunəmdə bu tələb olunmadı, çünki mən arxivi yerli dillərin axtarışından xaric etdim yerli-arxiv.

O localdef dəyişən vasitəsilə qovluğumdakı fayllarla işlədim I18NPATH giriş fayllarını axtarmaq üçün əlavə qovluq əlavə edə bilərsiniz və ikili faylları saxlamaq üçün qovluq əyri xətlərlə yol kimi göstərilə bilər:

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

POSIX içində olmasını təklif edir DİL siz yerli faylları olan qovluqlara mütləq yollar yaza bilərsiniz, slash ilə başlayaraq, lakin glibc в Linux bütün yollar bir dəyişən vasitəsilə ləğv edilə bilən əsas kataloqdan sayılır LOCPATH... Quraşdırıldıqdan sonra LOCPATH=~/.local/lib/locale/ lokalizasiya ilə bağlı bütün fayllar yalnız mənim qovluğumda axtarılacaq. Dəyişən təyin edildikdə yerli arxiv LOCPATH nəzərə alınmadı.

Budur son test:

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

Yaşasın! Biz bunu etdik!

Bəzi səhvlər

Mən əvvəlində qoyulmuş sətirlərin çeşidlənməsi ilə bağlı suallara artıq cavab vermişəm, lakin hələ də səhvlərlə bağlı bir neçə sual var - görünən və görünməyən.

Orijinal problemə qayıdaq.

Və proqram cür və proqram qoşulmaq -dən eyni simli müqayisə funksiyalarından istifadə edin glibc. Bu necə oldu qoşulmaq əmri ilə sıralanan sətirlərdə sıralama xətası verdi cür yerli en_US.UTF-8? Cavab sadədir: cür bütün sətri müqayisə edir və qoşulmaq yalnız ilk boşluq simvoluna qədər sətrin başlanğıcı olan açarı müqayisə edir. Mənim nümunəmdə bu, xəta mesajı ilə nəticələndi, çünki sətirlərdəki ilk sözlərin çeşidlənməsi tam sətirlərin çeşidlənməsinə uyğun gəlmədi.

Yerli "C" çeşidlənmiş sətirlərdə birinci boşluğa qədər aparıcı alt sətirlərin də çeşidlənəcəyinə zəmanət verir, lakin bu, yalnız xətanı maskalayır. Siz belə məlumatları (eyni soyadlı, lakin fərqli adları olan insanlar) götürə bilərsiniz ki, səhv mesajı olmadan faylların birləşdirilməsinin səhv nəticəsini verəcəkdir. İstəsək qoşulmaq fayl sətirlərini tam adı ilə birləşdirin, onda düzgün yol sahə ayırıcısını açıq şəkildə təyin etmək və bütün sətir üzrə deyil, əsas sahə üzrə çeşidləmək olardı. Bu halda birləşmə də düzgün işləyəcək və heç bir dildə xəta olmayacaq:

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

Uğurla icra edilən kodlanmış nümunə CP1251 başqa bir səhv ehtiva edir. Fakt budur ki, bütün paylamalarda mənə məlumdur Linux paketlərdə tərtib edilmiş yerli yoxdur en_RU.CP1251. Tərtib edilmiş yerli tapılmasa, o zaman cür bayt-bayt müqayisəsindən səssizcə istifadə edir, müşahidə etdiyimiz budur.

Yeri gəlmişkən, tərtib edilmiş yerlilərin əlçatmazlığı ilə əlaqəli başqa bir kiçik səhv var. Komanda LOCPATH=/tmp yerli -a bütün yerlilərin siyahısını qaytaracaq yerli-arxiv, lakin dəyişən dəsti ilə LOCPATH bütün proqramlar üçün (ən çox daxil olmaqla yerli) bu yerlilər mövcud olmayacaq.

$> 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

Nəticə

Əgər sətirlərin bayt toplusu olduğunu düşünməyə alışmış bir proqramçısınızsa, seçiminiz budur LC_COLLATE=C.

Əgər siz dilçi və ya lüğət tərtibçisisinizsə, o zaman dilinizi tərtib etsəniz yaxşı olar.

Sadə bir istifadəçisinizsə, o zaman sadəcə əmrə alışmaq lazımdır ls-a hərflə başlayan fayllarla kəsişmiş nöqtə ilə başlayan faylları çap edir və Midnight komandiriadları çeşidləmək üçün daxili funksiyalarından istifadə edən , nöqtə ilə başlayan faylları siyahının önünə köçürür.

References

Hesabat №10 Unicode harmanlama alqoritmi

Unicode.org saytında xarakter çəkiləri

ICU - IBM-dən Unicode ilə işləmək üçün kitabxananın tətbiqi.

Çeşidləmə testi ilə ICU

Xarakter çəkiləri ISO 14651

Ölçək fayl formatının təsviri ISO 14652

Simli müqayisə müzakirəsi glibc

Mənbə: www.habr.com

Добавить комментарий