Kuidas Linux'i sort sorteerib stringe

Sissejuhatus

KĂ”ik algas lĂŒhikesest skriptist, mis pidi ĂŒhendama teabe töötajate e-posti aadresside kohta, e-mail mis saadud e-posti loendi kasutajate nimekirjast, koos töötajate ametikohtadega, mis saadi personaliosakonna andmebaasist. MĂ”lemad nimekirjad eksporditi tekstifailidesse Unicode kodeeringuga, UTF-8 ja salvestati Unix'i ridade lĂ”ppudega.

Sisu mail.txt

Ivanov Andrei;ia@example.com

Sisu buhg.txt

Ivanova Alla;maaler
Jolkina Ella;kraanajuhiks
Ivanov Andrei;torumees
Abakanov Mihhail;maaler

Failide ĂŒhendamiseks sorteeriti need Unix'i kĂ€suga sort ja edastati Unix'i programmile, liitu, mis lĂ”petas ootamatult vea tĂ”ttu:

$> sort buhg.txt > buhg.srt
$> sort mail.txt > mail.srt
$> join buhg.srt mail.srt > result
join: buhg.srt:4: pole sorteeritud: Ivanov Andrei;torumees

Sorteerimise tulemuse visuaalne kontroll nĂ€itas, et ĂŒldiselt on sorteerimine Ă”ige, kuid meessoost ja naissoost perekonnanimede kattumiste korral on naised enne mehi:

$> sort buhg.txt
Abakanov Mihhail;maaler
Jolkina Ella;kraanajuhiks
Ivanova Alla;maaler
Ivanov Andrei;torumees

Tundub nagu Unicode sorteerimise viga vÔi nagu feminism sorteerimisalgoritmis. Esimene, muidugi, on usutavam.

JĂ€tame praegu kĂ”rvale liitu ja keskendume sort. Proovime ĂŒlesannet lahendada katsetamise meetodil. Alguseks muutkem kohaleks en_US jĂ€rgnevaga et_RU. Sorteerimiseks piisaks keskkonnamuutuja seadmisest LC_COLLATE, aga me ei jÀÀ selliste vĂ€ikeste asjadega vaeva:

$> LANG=et_RU.UTF-8 sort buhg.txt
Abakanov Mihhail;vÀrvija
Jolkina Ella;kraanajuhataja
Ivanova Alla;vÀrvija
Ivanov Andrei;torumees

Midagi ei ole muutunud.

Proovime faile ĂŒhebaidise kodeeringuga ĂŒmber kodeerida:

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

Taaskord ei ole midagi muutunud.

Midagi ei ole teha, tuleb leida lahendus internetist. Otseses mĂ”ttes venekeelsete perekonnanimede kohta pole midagi, aga on kĂŒsimusi teiste sorteerimise eriskummaliste asjade kohta. NĂ€iteks selline probleem: unix sort kĂ€sitleb ‘-‘ (kriipsu) mĂ€rke nĂ€htamatutena. LĂŒhidalt öeldes, read "a-b", "aa", "ac" sorteeritakse nagu "aa", "a-b", "ac".

Vastus on igal pool standardne: kasutage programmeerija kohalikke seadeid "C" ja siis on teil Ônn. Proovime:

$> LANG=C sort buhg.txt
Jolkina Ella;kraanajuhataja
Abakanov Mihhail;vÀrvija
Ivanov Andrei;torumees
Ivanova Alla;advokaat

Midagi on muutunud. Ivanov’id on Ă”iges jĂ€rjekorras, kuid Jolkina on kuhugi kadunud. Naaseme algsele ĂŒlesandele:

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

See toimis ilma vigadeta, nagu lubas internet. Ja see on vaatamata Ёlkini esimesele reale.

Probleem nĂ€ib olevat lahendatud, kuid igaks juhuks proovime veel ĂŒht vene kodeeringut — Windowsi kodeeringut CP1251:

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

Sorteerimise tulemus, nagu imelik see ka ei oleks, vastab lokaadile "C", ja kogu nĂ€ide seega lĂ€bib ilma vigadeta. Milline mĂŒstika.

Ma ei armasta mĂŒstikat programmeerimises, kuna see varjab tavaliselt vigu. Pean tĂ”siselt tegelema kĂŒsimusega, kuidas see töötab sort ja millele see mĂ”ju avaldab LC_COLLATE .

LĂ”puks pĂŒĂŒan vastata kĂŒsimustele:

  • miks sorteeriti valejĂ€rjekorras naissoost perekonnanimesid
  • miks LANG=ru_RU.CP1251 osutus ekvivalendiks LANG=C
  • miks on sort ja liitu erinevad arusaamad sorteeritud ridade jĂ€rjekorrast
  • miks kĂ”igis mu nĂ€idetes on vigu
  • lĂ”puks, kuidas sorteerida ridu oma maitse jĂ€rgi

Sorteerimine Unicode'is

Esimene peatus on tehniline aruanne nr 10 pealkirjaga Unicode collation algorithm veebisaidil unicode.org. Aruanne sisaldab palju tehnilisi detaile, seega luban endale tuua lĂŒhikese ĂŒlevaate pĂ”hiteemadest.

Sorteerimine — "string comparison" on the basis of any sorting algorithm. The algorithms can differ ("bubble", "merge", "quick"), but they all rely on comparing pairs of strings to establish their order.

Sorting strings in natural language is quite a complex issue. Even in the simplest single-byte encodings, the order of letters in an alphabet that differs from the English Latin alphabet won't match the numerical values by which these letters are encoded. For example, in the German alphabet, the letter Ö is positioned between Umbes ja P, while in the encoding CP850 it falls between ÿ ja Ü.

One could try to abstract from a specific encoding and consider "ideal" letters arranged in a certain order, as done in Unicode. Encodings UTF8, UTF16 or single-byte KOI8-R (if a limited subset of Unicode is needed) will provide different numerical representations of letters but will refer to the same elements in the basic table.

Selgub, et isegi sĂŒmbolite tabeli nullist koostamisel ei suuda me sellele mÀÀrata universaalset sĂŒmbolite jĂ€rjestust. Erinevates rahvuslikes tĂ€hestikes, mis kasutavad samu tĂ€hti, vĂ”ib nende tĂ€hede jĂ€rjestus erineda. NĂ€iteks prantsuse keeles Æ peetakse ligatuuriks ja seda sorteeritakse kui stringi. AETaas, norski keeles Æ on see eraldi tĂ€ht, mis paikneb pĂ€rast Z. Muide, lisaks ligatuuridele nagu Æ on olemas tĂ€hed, mis on kirjutatud mitme sĂŒmboliga. Nii on TĆĄehhi tĂ€hestikus tĂ€ht Ch, mis asub nende vahel H ja I.

Lisaks tĂ€hestike erinevustele on olemas ka teised rahvuslikud traditsioonid, mis mĂ”jutavad sorteerimist. Eriti tĂ”statub kĂŒsimus: millises jĂ€rjekorras peaksid sĂ”naraamatutes esinduma suurtĂ€hed ja vĂ€iketĂ€hed? Samuti vĂ”ivad sorteerimist mĂ”jutada erimĂ€rgid. Hispaania keeles asetatakse kĂŒsimuse algusesse pööratud kĂŒsimĂ€rgi mĂ€rk (ÂżTe gusta la mĂșsica?). Sel juhul on ilmne, et kĂŒsimustikud ei peaks olema eraldi rĂŒhmitatud vĂ€ljaspool tĂ€hestikku, vaid kuidas jĂ€rjestada ridu koos teiste kirjavahemĂ€rgiga?

Ma ei kavatse peatuda ridade jĂ€rjestamisele keeltes, mis erinevad tugevasti Euroopa keeltest. MĂ€rgin, et paremale vĂ”i ĂŒlespoole kirjutamise suunaga keeltes hoitakse sĂŒmboleid ridades tĂ”enĂ€oliselt lugemisjĂ€rjestuses, ning ka mitte-tĂ€heline kirjutamine omab oma viise ridade sĂŒmbolite jĂ€rjekorra seadmiseks. NĂ€iteks vĂ”ivad hieroglĂŒfid olla jĂ€rjestatud vastavalt kirjutusviisile (hiina hieroglĂŒfide vĂ”tmed) vĂ”i hÀÀldusele. Kuidas emojid peaksid olema jĂ€rjestatud, ma ausalt öeldes ei tea, kuid ka nende jaoks vĂ”iks midagi vĂ€lja mĂ”elda.

Ülaltoodud omaduste pĂ”hjal on vĂ€lja töötatud peamised nĂ”uded ridade vĂ”rdlemiseks, mis pĂ”hinevad Unicode tabelitel:

  • ridade vĂ”rdlemine ei sĂ”ltu sĂŒmbolite asukohast kooditabelis;
  • sĂŒmbolite jĂ€rjestused, mis moodustavad ĂŒhe sĂŒmboli, viiakse kanonilisse vormi (A + ĂŒlemine ring see on sama mis Å);
  • stringide vĂ”rdlemisel vĂ”etakse sĂŒmbol arvesse kontekstis ja vajadusel ĂŒhendatakse see naabersĂŒmbolitega ĂŒheks vĂ”rdlemiseĂŒksuseks (Ch tĆĄehhi keeles) vĂ”i jagatakse mitmeks (Æ prantsuse keeles);
  • kĂ”ik rahvuslikud omadused (tĂ€hestik, suure-/vĂ€ikese tĂ€htede erinevus, kirjavahemĂ€rgid, kirjutamise viiside jĂ€rjekord) peavad olema seadistatavad kuni kĂ€sitsi mÀÀratud jĂ€rjekorrani (emoji);
  • vĂ”rdlemine on oluline mitte ainult sortimise jaoks, vaid ka paljude teiste kohtade jaoks, nĂ€iteks stringide vahemike mÀÀramiseks (asendus {A
 я} in bash);
  • vĂ”rdlemine peab toimuma piisavalt kiiresti.

Lisaks on aruande autorid formuleerinud vÔrdlemise omadused, millele algoritmi arendajad ei peaks tuginema:

  • vĂ”rdlemise algoritm ei tohiks nĂ”uda iga keele jaoks eraldi sĂŒmbolite kogumit (vene ja ukraina keel kasutavad suuresti samu kirillitsa sĂŒmboleid);
  • vĂ”rdlemine ei tohiks tugineda sĂŒmbolite jĂ€rjekorrale Unicode tabelites;
  • stringi kaal ei tohiks olla stringi atribuut, kuna sama string erinevates kultuurilistes kontekstides vĂ”ib omada erinevat kaalu;
  • tekstide kaalud vĂ”ivad muutuda sulandumisel vĂ”i jagunemisel (nĂ€iteks x < y ei tĂ€henda, et xz < yz);
  • erinevad tekstid, millel on samad kaalud, loetakse sortimisalgoritmi seisukohalt vĂ”rdsed. TĂ€iendava jĂ€rjestuse kehtestamine nende tekstide vahel on vĂ”imalik, kuid see vĂ”ib halvendada jĂ”udlust;
  • korduvate sortimiste puhul vĂ”ivad vĂ”rdselt kaalutud tekstid omavahel kohtasid vahetada. Stabiilsus on konkreetse sortimisalgoritmi omadus, mitte tekstide vĂ”rdlemise algoritmi omadus (vt eelmist punkti);
  • sortimisreeglid vĂ”ivad aja jooksul muutuda kultuuriliste traditsioonide tĂ€psustamise/muutmise tĂ”ttu.

Samuti on sÀtestatud, et vÔrdlemisalgoritm ei tea midagi vÔrreldavate tekstide semantikast. Seega ei tohiks numbritest koosnevaid tekstid vÔrrelda numbritena ning ingliskeelsetes nimedes ei tohi artiklit vÀlja jÀtta (Beatles, The).

KÔikide tÔstatatud nÔuete rahuldamiseks on vÀlja töötatud mitmetasandiline (tegelikult neljatase) tabeli sortimisalgoritm.

Eelnevalt tuuakse stringi sĂŒmbolid kanoniliseks ja rĂŒhmitatakse vĂ”rdlemiseks ĂŒhikuteks. Iga vĂ”rdlusĂŒhikule omistatakse mitu kaalu vastavalt erinevatele vĂ”rdlustasanditele. VĂ”rdlusĂŒksuste kaalud on jĂ€rjestatud hulkade elemendid (antud juhul tĂ€isarvud), mida saab vĂ”rrelda suurem-vĂ€hem suhtega. Eriline vÀÀrtus IGNORED (0x0) tĂ€hendab, et vastaval vĂ”rdlustasandil antud ĂŒksus ei osale vĂ”rdluses. Stringide vĂ”rdlemine vĂ”ib toimuda korduvalt, kasutades vastavate tasemete kaalu. Iga taseme vĂ”rdlusĂŒhikute kaalu vĂ”rreldakse jĂ€rjestikku omavahel.

Erinevates algoritmi rakendustes eri rahvuslike traditsioonide jaoks vĂ”ivad koefitsientide suurused erineda, kuid Unicode'i standardisse kuulub pĂ”hikaalu tabel — "Default Unicode Collation Element Table" (DUCET). Tahan mĂ€rkida, et muutuja seadistamine LC_COLLATE on tegelikult viide kaalutabeli valimisele stringide vĂ”rdluse funktsioonis.

Kaalukoefitsiendid DUCET on organiseeritud jÀrgmiselt:

  • esimese taseme puhul muudetakse kĂ”ik tĂ€hed ĂŒhte soovitud suurusesse, diakriitilised mĂ€rgid jĂ€etakse vĂ€lja, kirjavahemĂ€rgid (kuid mitte kĂ”ik) ignoreeritakse;
  • teise taseme puhul arvestatakse ainult diakriitilisi mĂ€rke;
  • kolmanda taseme puhul arvestatakse ainult tĂ€hte suurust;
  • neljanda taseme puhul arvestatakse ainult kirjavahemĂ€rke.

VÔrdlemine toimub mitmes etapis: kÔigepealt vÔrreldakse esimesel tasemel koefitsiente; kui kaalud kokkusobivad, jÀrgneb uuesti vÔrreldes teisel tasemel; seejÀrel vÔib lÀbi viia ka kolmanda ja neljanda taseme.

VĂ”rdlemine lĂ”peb, kui ridades leidub vastanduvad vĂ”rdlemise ĂŒksused erinevate kaalu jĂ€rgi. Ridu, millel on kĂ”ikidel neljal tasemel vĂ”rdne kaal, peetakse ĂŒksteisega vĂ”rdsed.

See algoritm (koos paljude lisatehniliste detailidega) andis aruande nr 10 nime — "Unicode Collation Algorithm" (UCA).

Selles kohas muutub meie nÀite sortimis kÀitumine natuke arusaadavamaks. Oleks hea vÔrrelda seda Unicode'i standardiga.

Teostuste testimiseks UCA on olemas spetsiaalne test, kasutades kaalu faili, mis rakendab DUCET. Kaalu failis on erinevaid huvitavaid asju. NĂ€iteks on seal mĂ€nge mahjongi ja Euroopa domino joonestus ning kaardikomplekti mastide jĂ€rjestus (sĂŒmbol 1F000 ja edasi). Kaardimastid on paigutatud vastavalt bridĆŸi reeglitele — PCHBT, ning mastidesse kuuluvad kaardid on jĂ€rjestatud jĂ€rjehoidjate jĂ€rgi T, 2, 3
 K.

Reaalse ridade sortimise Ă”iguse kontrollimine vastavalt DUCET oleks ĂŒsna kurnav, kuid Ă”nneks meie jaoks on olemas suurepĂ€rane rakendus Unikoodi raamatukogu jaoks — "International Components for Unicode" (ICU).

Selle raamatukogu veebisaidil, mis on loodud IBM, on demonstreerivad lehed, sealhulgas stringide vÔrdlemise algoritmi leht. Sisestame meie testitavad stringid vaikeseadistustega ja, oh imet, saame ideaalse vene sortimise.

Abakanov Mihhail;vÀrvimees
Jolkina Ella;kraanaoperaator
Ivanov Andre;torumees
Ivanova Alla;advokaat

Muide, saidilt ICU vÔib leida tÀpsustusi vÔrdlemise algoritmi töö kohta kirjavahemÀrkide kÀsitlemisel. NÀiteks Collation FAQ ignoreeritakse apostroof ja sidekriips.

Unikood aitas meid, kuid kummalise kĂ€itumise pĂ”hjuseid sort ĂŒhes Linux peab otsima mujalt.

Sortimine glibc

Kiire ĂŒlevaade tööriista lĂ€htekoodidest sort kohast GNU Core Utils nĂ€itas, et tööriist ise lokaliseerimine piirneb praeguse muutuja vÀÀrtuse prindimisega LC_COLLATE kĂ€ivitamisel silumisreĆŸiimis:

$ sort --debug buhg.txt > buhg.srt
sort: kasutab ‘en_US.UTF8’ sorteeringureegleid

Stringide vÔrdlemine toimub standardse funktsiooni strcoll, seega on kÔik huvitav juba raamatukogus glibc.

VDS-l on vĂ”imalik installida: wiki projekt glibc stringide vĂ”rdlemisega tegeleb ĂŒks lĂ”ik. Sellest lĂ”igust vĂ”ib jĂ€reldada, et glibc sorteerimine pĂ”hineb juba tuttaval algoritmil UCA (The Unicode collation algorithm) ja/vĂ”i sarnasel standardil ISO 14651 (Rahvusvaheline stringide jĂ€rjestamine ja vĂ”rdlemine). Mis puudutab viimast standardit, siis tuleks mĂ€rkida, et saidil standards.iso.org ISO 14651 on ametlikult kuulutatud avalikuks, kuid vastav link viib olematusse lehte. Google annab mitu lehte, kus on lingid ametlikele saitidele, mis pakuvad standardi elektroonilise koopia ostmist sada eurot, kuid kolmanda-neljanda lehe otsingutulemustes leiab ka otse lingid PDF. Üldiselt ei erine standard praktiliselt UCA, kuid loetakse igavamalt, kuna see ei sisalda silmapaistvaid nĂ€iteid riiklike eripĂ€rade sorteerimisel.

KĂ”ige huvitavam teave oli wiki link vea jĂ€lgimise sĂŒsteemile stringide vĂ”rdlemise rakendamise arutamiseks glibc. Arutelust selgub, et glibc stringide vĂ”rdlemiseks kasutatakse ISOĂŒht tabelit Common Template Table (CTT), mille aadressi leiate rakendusest A standardist ISO 14651. Aastatel 2000 kuni 2015 ei olnud sellel tabelil hooldajat ja see erineb mĂ€rgatavalt (vĂ€hemalt visuaalselt) praegusest standardi versioonist. Aastatel 2015 kuni 2018 toimus kohandamine uue versiooni tabeliga ja hetkel on teil vĂ”imalus kohtuda reaalses elus nii uue versiooniga ( glibc ) kui ka vanaga (CentOS 8NĂŒĂŒd, kui kogu teave algoritmi ja abitablettide kohta on olemas, saame naasta algse probleemi juurde ja mĂ”ista, kuidas Ă”igesti jĂ€rjestada stringe vene lokaalis.CentOS 7).

ISO 14651/14652

Meid huvitava tabeli lÀhtekood

enamikus distributsioonides CTT asub kataloogis Linux . Ainult tabel asub failis /usr/share/i18n/locales/iso14651_t1_common . SeejÀrel kaasatakse see fail direktiivigacopy iso14651_t1_common faili iso14651_t1 , mis omakorda on kaasatud riiklikele failidele, sealhulgas. Enamikus distributsioonides en_US ja et_RU. Enamikus jaotustes Linux kÔik algfailid on lisatud pÔhivaru, kuid kui neid pole, peate installima tÀiendava paketi jaotusest.

Faili struktuur , mis omakorda on kaasatud riiklikele failidele, sealhulgas see vĂ”ib tunduda kohutavalt sĂ”nakas, ebamugavate nimevormingureeglitega, kuid kui vaadata, on see piisavalt lihtne. Struktuur on kirjeldatud standardis ISO 14652, mida saab alla laadida veebisaidilt open-std.org. Veel ĂŒht faili formaadi kirjelduse saab lugeda spetsifikatsioonides POSIX alates OpenGroup. Standardi lugemise alternatiivina saab uurida funktsiooni allikaid collate_read ĂŒhes glibc/locale/programs/ld-collate.c.

Faili struktuur nÀeb vÀlja jÀrgmine:

Vaikimisi kasutatakse sĂŒmbolit kui maskeerimissĂŒmbolit ning reavahetus sĂŒmbolist # on kommentaar. MĂ”lemat sĂŒmbolit saab ĂŒle kirjutada, nagu on tehtud uues tabeli versioonis:

escape_char /
comment_char %

Failis vĂ”ivad esineda tokenid formaadis <Uxxxx> vĂ”i <Uxxxxxxxx> (kus x — kuusnurkne number). See on kuusnurkne esitus Unicode'i koodipunktide UCS-4 (UTF-32). KĂ”ik ĂŒlejÀÀnud elemendid nurgasulgudes (sealhulgas <Uxxxx_xxxx>, <2> ja sarnased) on peetud lihtsakste stringikonspektideks, millel ei ole konteksti vĂ€ljaspool erilist tĂ€hendust.

String LC_COLLATE ĂŒtleb meile, et edasi algavad andmed, mis kirjeldavad stringide vĂ”rdlemist.

Esiteks mÀÀratakse kaalu nimed vĂ”rdlustabelis ning tĂ€hemĂ€rkide kombinatsioonide nimed. Üldiselt kuuluvad kaks tĂŒĂŒpi nimesid kahele erinevale entiteedile, kuid tegelikus failis on need segamini. Kaalu nimed mÀÀratakse mĂ€rksĂ”naga collating-symbol (vĂ”rdlemise sĂŒmbol), kuna Unicode'i sĂŒmbolid, millel on samad kaalud, peetakse ekvivalentseteks sĂŒmboliteks.

Jooksva faili versiooni sektsiooni kogupikkus on umbes 900 rida. Olen vĂ”tnud nĂ€iteid mitmest kohast, et nĂ€idata nimede meelevaldsust ja mitut tĂŒĂŒpi sĂŒntaksit.

LC_COLLATE

collating-symbol 
collating-symbol 
collating-symbol 
collating-symbol 
...
collating-symbol 
collating-symbol 
collating-symbol 
...
collating-symbol ..
collating-symbol  % garantii suurim sĂŒmbolivÀÀrtus. Hoidke selle nimekirja lĂ”pus
...
collating-element  from ""
collating-element  from ""

  • collating-symbol registreerib stringi OSMANYA kaalu nimede tabelis
  • collating-symbol .. registreerib nimejĂ€rjestuse, mis koosneb eesliitest S ja kuuekĂŒmnendkĂŒmnendast numbrilisest sufiksist alates 1D000 kuni 1D35F.
  • FFFF ĂŒhes collating-symbol nĂ€eb vĂ€lja nagu suur mĂ€rkidevaheline tĂ€isarv kuuekĂŒmnendkĂŒmnendises sĂŒsteemis, kuid <SFFFF> on lihtsalt nimi, mis vĂ”iks vĂ€lja nĂ€ha nagu <VERYBIGVAL>
  • nimi <U0413> tĂ€hendab koodpunkti kodeeringus UCS-4
  • collating-element from "" registreerib uue nime kahele Unicode'i punktile.

Kui kaalud on mÀÀratud, mÀÀratakse tegelikud kaalud. Kuna vĂ”rdlemisel on oluline ainult suuremate-vĂ€iksemate suhted, mÀÀratakse kaalud lihtsa nime loetelu jĂ€rgi. Esiteks loetletakse "kergemad" kaalud, seejĂ€rel "raskaamad". Pean meenutama, et igale Unicode'i sĂŒmbolile antakse neli erinevat kaalu. Need on koondatud ĂŒhte jĂ€rjestatud jĂ€rjestusse. Teoreetiliselt vĂ”ib iga sĂŒmboolne nimi olla kasutatud mis tahes neljal tasemel, kuid kommentaarides mĂ€rgitakse, et arendajad mĂ”tlevad nimed tasemetena.

% SĂŒmbolite kaalude mÀÀramine

% Kolmanda taseme kaalude mÀÀramine




...
% Teise taseme kaalude mÀÀramine

 % ÜHENDAMINE MADALALINNA
 % ÜHENDAMINE KOMA ÜLE
 % ÜHENDAMINE PÖÖRDKOMA ÜLE
...
% Esimese taseme kaalude mÀÀramine
 % HORIZONTAL TABULATSIOON
 % RIDA KATKESTUS
 % VERTIKAALNE TABULATSIOON
...
 % KÜRILI KERGE TÄHE DE
 % KÜRILI KERGE TÄHE KOMI DE
 % KÜRILI KERGE TÄHE DJE
 % KÜRILI KERGE TÄHE KOMI DJE
 % KÜRILI KERGE TÄHE GJE
 % KÜRILI KERGE TÄHE ZE KAEGA
 % KÜRILI KERGE TÄHE IE
 % KÜRILI KERGE TÄHE IE BREVEGA
 % KÜRILI KERGE TÄHE UKRAINLAINE IE
 % KÜRILI KERGE TÄHE ZHE

LÔpuks on ise kaalu tabel.

Kaalude sektsioon on raamis koos mĂ€rksĂ”nadega order_start ja order_end. TĂ€iendavad parameetrid order_start mÀÀravad, millises suunas ridasid igal vĂ”rreldava tasemel vaadataks. VaikesĂ€tetena kasutatakse parameetrit forward. Sektsiooni sisu koosneb ridadest, mis sisaldavad sĂŒmbolikoodi ja nelja selle kaalu. SĂŒmbolikood vĂ”ib olla esindatud sĂŒmbol ise, koodipunkt vĂ”i varasemalt mÀÀratletud sĂŒmboolne nimi. Kaaled saab samuti mÀÀrata sĂŒmboolsete nimede, koodipunktide vĂ”i ise sĂŒmbolite kaudu. Kui kasutatakse koodipunkte vĂ”i sĂŒmboleid, on nende kaal kooskĂ”las koodipunkti numbrilise vÀÀrtusega (positsioonis Unicode tabelis). Ekslikult mitteĂŒles mĂ€rgitud sĂŒmbolid (nagu ma aru saan) loetakse tabelis, mille esmane kaal vastab nende positsioonile Unicode tabelis. Eriline kaal vÀÀrtus IGNORE tĂ€hendab, et vastaval vĂ”rdlustasemel antud sĂŒmbolit ignoreeritakse.

Kaalustruktuuri demonstreerimiseks valisin kolm piisavalt selget fragmenti:

  • sĂŒmbolid, mida ignoreeritakse tĂ€ielikult
  • sĂŒmbolid, mis on ekvivalentne numbriga kolm esimesel kahel tasemel
  • vene tĂ€hestiku algus, mis ei sisalda diakriitilisi mĂ€rke ja seetĂ”ttu sorteeritakse peamiselt esimese ja kolmanda taseme jĂ€rgi.

tellimus_algus edasi;edasi;edasi;edasi,positsioon
 IGNORE;IGNORE;IGNORE;IGNORE % NULL (in 6429)
 IGNORE;IGNORE;IGNORE;IGNORE % PEALKIRJA ALGUS (in 6429)
 IGNORE;IGNORE;IGNORE;IGNORE % TEKSTI ALGUS (in 6429)
...
 ;; ;  % KOLMOND DIGIT 
 ;; ;  % TÄIESTI KOLME FULLWIDTH 
 ;; ;  % KOLMNEDA PÄRA KOLM 
 ;; ;  % KOLME KOLME PUNKT 
 ;; ;  % MATEMAACOLILLA BOLD DIGIT KOLM
...
 ;; ;  % KIRILL SILMINE A 
 ;; ;  % KIRILL SUUR A 
 ;; ;  % KIRILL SILMINE A KÄRGIK 
 ;; ;  % KIRILL SILMINE A KÄRGIK
...
 ;; ;  % KIRILL SILMINE BE 
 ;; ;  % KIRILL SUUR BE 
 ;; ;  % KIRILL SILMINE VE 
 ;; ;  % KIRILL SUUR VE
...
tellimus_lÔpp

NĂŒĂŒd on taas vĂ”imalik naasta artikli alguses esitatud nĂ€idete jĂ€rjestusse. Probleem peitub selles osas kaalu tabelis:

IGNORE;IGNORE;IGNORE; % RUUM 
 IGNORE;IGNORE;IGNORE; % KÜSIMUSMÄRK 
 IGNORE;IGNORE;IGNORE; % TSITAAT
...

On mĂ€rgata, et selles tabelis ignoreeritakse interpunktsiooni tavaliselt, ASCII (sealhulgas tĂŒhik) stringide vĂ”rdlemisel. Eranditeks on vaid stringid, mis on tĂ€ielikult identsed, vĂ€lja arvatud vastavates positsioonides esinevad interpunktsioonimĂ€rgid. Minu nĂ€ite stringid (pĂ€rast sorteerimist) nĂ€evad vĂ€lja jĂ€rgmiselt:

AbakanovMikhailPainters
YolkinaElenaScreenwriter
IvanovaAllaPainter
IvanovAndreyJoiner

Arvestades, et kaalutabelis on vene keeles suured tÀhed vÀikeste tÀhtede jÀrel (kolmandal tasemel <CAP> kÔvemad kui <MIN>), siis nÀeb sorteerimine vÀlja tÀiesti korrektne.

Muutes muutuja LC_COLLATE=C laaditakse eriline tabel, mis mÀÀrab byte-taseme vÔrdlemise

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'fe', L'xff'
};

Kuna Unicode'is asub koodipunkt Ё koha jÀrjekorras A ees, jÀrjestatakse ka stringid vastavalt.

Teksti- ja binaartabelid

On selge, et stringide vĂ”rdlemine on ÀÀrmiselt sage toiming, samas kui tabeli analĂŒĂŒs CTT on ĂŒsna kulukas protseduur. Tabeli juurdepÀÀsu optimeerimiseks kompileeritakse see kahekordsesse vormingusse kĂ€suga localedef.

Meeskond localedef millele antakse parameetritena fail, mis sisaldab riiklike eripĂ€rade tabelit (valik -i), milles kĂ”ik sĂŒmbolid on esitatud Unicode'i punktidena, ja fail, mis seob Unicode'i punkte konkreetse kodeeringu sĂŒmbolitega (valik -f). Töö tulemusena luuakse kohalikuks kaksikfailid, mille nimi on mÀÀratud viimasena antud parameetris.

Glibc toetab kaht tĂŒĂŒpi kahekordse faili formaate: "traditsiooniline" ja "modernne".

Traditsiooniline formaat eeldab, et lokaali nimi on alamkausta nimi /usr/lib/locale/. Selles alamkaustas hoitakse kaksikfailid LC_COLLATE, LC_CTYPE, LC_TIME ja nii edasi. Fail LC_IDENTIFICATION sisaldab lokaali formaalset nime (mis vÔib erineda kausta nimest) ja kommentaare.

Kaasaegne formaat eeldab, et kĂ”ik lokaalid on salvestatud ĂŒhte arhiivi /usr/lib/locale/locale-archive, mis on kaardistatud kĂ”igi protsesside virtuaalsesse mĂ€lu, mis seda kasutavad. glibc. Keelja on kaasaegses formaadis lĂ€bi teinud teatud kanoniseerimise – kodeeringu nimedes jÀÀvad alles vaid numbrid ja tĂ€hed, mis on muudetud vĂ€iketĂ€htedeks. Nii ru_RU.KOI8-R, salvestatakse jĂ€rgmiselt ru_RU.koi8r.

Sisendfailid otsitakse praegusest kataloogist ning ka kataloogidest /usr/share/i18n/locales/ ja /usr/share/i18n/charmaps/ failide CTT ja kodeerimisfailide jaoks vastavalt.

NÀiteks kÀsk

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

kompileerib faili /usr/share/i18n/locales/ru_RU kasutades kodeerimisfaili /usr/share/i18n/charmaps/MAC-CYRILLIC.gz ja salvestab tulemuse /usr/lib/locale/locale-archive nimega ru_RU.maccyrillic

Kui seadistada muutujaks LANG=en_US.UTF-8 siis glibc otsitakse lokaali binaarfailide jaoks jÀrgmises failide ja kataloogide jÀrjestuses:

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

Kui lokaal esineb nii traditsioonilises kui ka kaasaegses formaadis, antakse eelis kaasaegsele.

Kompileeritud lokaalide loendit saab vaadata kÀsuga locale -a.

Oma vÔrdlustabeli koostamine

NĂŒĂŒd, relvastatuna teadmistega, on vĂ”imalik luua oma ideaalne stringide vĂ”rdlustabel. See tabel peab Ă”igesti vĂ”rdlema vene tĂ€hti, sealhulgas tĂ€hti Ё, ning arvestama loetlemismĂ€rke vastavalt tabeliga ASCII.

Oma sortimistabeli ettevalmistamise protsess koosneb kahest etapist: kaalutabeli redigeerimisest ja selle kompileerimisest binaarvormis kÀsuga localedef.

Et vÔrdlustabelit oleks vÔimalik kohandada minimaalse redigeerimiskuluga, on formaadis ISO 14652 ette nÀhtud juba olemasoleva tabeli kaalude kohandamise sektsioonid. Sektsioon algab vÔtmesÔnast reorder-after ja positsiooni nÀitamisest, mille jÀrel toimub asendamine. Sektsiooni lÔpetab rida reorder-end. Kui on vaja korrigeerida mitu tabeli osa, luuakse iga sellise osa jaoks oma sektsioon.

Kopeerisin uued versioonid failidest . SeejĂ€rel kaasatakse see fail direktiiviga ja et_RU hoidlast glibc oma kodukatalooge ~/.local/share/i18n/locales/ ja muutsin veidi jaotust LC_COLLATE ĂŒhes et_RU. Uued faili versioonid on tĂ€ielikult ĂŒhilduvad minu versiooniga glibc. Kui soovite kasutada vanu faili versioone, peate muutma sĂŒmboolseid nimesid ja kohta, kust asendamine tabelis algab.

LC_COLLATE
% Kopeeri mall ISO/IEC 14651
kopeerida "iso14651_t1"
jÀrjesta-pÀrast 
 ;;; % TĂŒhi ruum
 ;;; % Üksik mĂ€rk
 ;;; % TsiteerimismÀrk
...
 ;;; % Parem kurv
 ;;; % Tilde
jÀrjesta-lÔpp
LÕPP LC_COLLATE

Tegelikult oleks pidanud muutma vÀljad LC_IDENTIFICATION niimoodi, et need viitaksid lokaalile ru_MY, kuid minu nÀites ei olnud see vajalik, kuna ma vÀlistasin otsingust lokaliseerimise arhiivi locale-archive.

Et localedef töötasin failidega oma kaustas lÀbi muutuja I18NPATH vÔib lisada tÀiendava katalooge sisendfailide otsimiseks, ja katalooge binaarfailide salvestamiseks vÔib mÀÀrata teena, mis sisaldab kaldkriipse:

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

POSIX eeldab, et LANG vĂ”ib kirjutada absoluutsete teedena kataloogidele, kus asuvad lokaalid, mis algavad otsekaldkriipsuga, kuid glibc ĂŒhes Linux kĂ”ik teed arvestatakse baas-kataloogist, mille vĂ”ib ĂŒletada muutuja LOCPATH. PĂ€rast seadistamist LOCPATH=~/.local/lib/locale/ otsitakse kĂ”iki lokaliseerimisega seotud faile ainult minu kaustast. Lokaalide arhiiv, kui muutuja on seadistatud. LOCPATH ei arvestata.

See test on:

$> LANG=et_EE.UTF-8 LOCPATH=~/.local/lib/locale/ sort buhg.txt
Abakanov Mihhail;vÀrvija
Jolkina Ella;kraanajuht
Ivanov Andrei;sepp
Ivanova Alla;advokaat

Hurraa! Me tegime selle Àra!

Vigade parandamine

Olen juba vastanud alguses esitatud stringi sortimise kĂŒsimustele, kuid veel on mĂ”ned kĂŒsimused nĂ€htavatest ja nĂ€htamatutest vigadest.

Naaseme algse ĂŒlesande juurde.

Ja programm sort ja programm liitu kasutavad samu stringi vĂ”rdlemise funktsioone glibc. Kuidas juhtus, et liitu andis sortimisvea stringidele, mis sorteeriti kĂ€suga sort jĂ€rgi lokaalis et_US.UTF-8? ОтĐČДт ĐżŃ€ĐŸŃŃ‚: sort vĂ”rdleb stringi tervikuna, samas kui liitu vĂ”rdleb ainult vĂ”tit, mis vaikimisi on stringi algus kuni esimese tĂŒhikuni. Minu nĂ€ites viis see veateadeni, kuna lause algusede sortimine ei kattunud tĂ€ielike stringide sortimisega.

Lokaal "C" garanteerib, et sorteeritud ridade algusosad kuni esimese tĂŒhikuni on samuti sorteeritud, kuid see varjab viga. On vĂ”imalik leida selliseid andmeid (inimesed, kellel on sama perekonnanimi, kuid erinevad eesnimed), mis ilma veateateta tooksid vale tulemuse failide ĂŒhinemisel. Kui soovime, et liitu ĂŒhendaks failide ridu FIO jĂ€rgi, siis Ă”ige viis on selgelt mÀÀratleda vĂ€ljade eraldaja ja sorteerida vĂ”tmevĂ€lja jĂ€rgi, mitte kogu rida jĂ€rgi. Sel juhul toimub ĂŒhinemine Ă”igesti ja ei esine vigu ĂŒheski lokaadis:

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

Edukalt lĂ€bi viidud nĂ€ide kodeeringus CP1251 sisaldab veel ĂŒhte viga. Asi on selles, et kĂ”igis tuntud distributsioonides Linux puuduvad pakettides kompileeritud lokaalid ru_RU.CP1251. Kui kompileeritud lokaali ei leita, siis sort kasutab vaikselt byte-per-byte vĂ”rdlemist, mida me ka jĂ€lgisime.

Muide, on veel ĂŒks vĂ€ike viga, mis on seotud kompileeritud lokaalide kĂ€ttesaamatusega. KĂ€sk LOCPATH=/tmp locale -a vĂ€ljastab kĂ”ik lokaalid locale-archive, kuid eeldusel, et muutujat on seadistatud LOCPATH kĂ”ikidele programmidele (ka kĂ”igele locale) need lokaalid ei ole saadaval.

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

KokkuvÔte

Kui oled programmeerija, kes on harjunud arvama, et stringid on baitide kogum, siis on see sinu valik. LC_COLLATE=C.

Kui oled lingvist vÔi sÔnaraamatute koostaja, siis oleks parem, kui compileeriksid oma lokaali.

Kui oled lihtsalt kasutaja, siis piisab, kui harjuda, et kÀsk ls -a vÀljastab faile, mis algavad punktiga, segatuna failidega, mis algavad tÀhega, ning Midnight Commander, mis kasutab oma sisemisi funktsioone nimede sortimiseks, toob failid, mis algavad punktiga, nimekirja algusesse.

Lingid

Aruanne nr 10 Unicode kollektsiooni algoritm

TĂ€htede kaaluudised unicode.org

ICU — IBM-i Unicode'i töötlusraamatukogu rakendus.

Sortimise test ICU

TĂ€htede kaalud ISO 14651

Failivormingu kirjeldus kaaludega ISO 14652

Arutelu stringide vĂ”rdlemise ĂŒle glibc

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster