Kiel la ordigo de Linukso ordigas ŝnurojn

Enkonduko

Ĉio komenciĝis per mallonga skripto, kiu devis kombini adresinformojn retpoŝto dungitoj akiritaj de la listo de dissendolisto-uzantoj, kun dungitpostenoj akiritaj de la HR-sekcia datumbazo. Ambaŭ listoj estis eksportitaj al Unikodaj tekstdosieroj UTF-8 kaj konservita kun Unikso-liniaj finaĵoj.

Enhavo mail.txt

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

Enhavo buhg.txt

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

Por kunfandi, la dosieroj estis ordigitaj per la Unikso-komando varo kaj submetita al la enigo de la Unikso-programo aliĝi, kiu neatendite malsukcesis kun eraro:

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

Vidante la ordigan rezulton per viaj okuloj montris, ke, ĝenerale, la ordigo estas ĝusta, sed en la kazo de koincidoj de viraj kaj inaj familinomoj, la inaj venas antaŭ la viraj:

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

Aspektas kiel ordiga eraro en Unikodo aŭ kiel manifestiĝo de feminismo en la ordiga algoritmo. La unua estas, kompreneble, pli kredinda.

Ni lasu ĝin flanken por nun aliĝi kaj koncentriĝi sur varo. Ni provu solvi la problemon per scienca pikado. Unue, ni ŝanĝu la lokon de eo_US sur ru_RU. Por ordigi, sufiĉus agordi la mediovariablon LC_COLLATE, sed ni ne perdos tempon por bagateloj:

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

Nenio ŝanĝiĝis.

Ni provu rekodi la dosierojn en unu-bajtan kodigon:

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

Denove nenio ŝanĝiĝis.

Vi povas fari nenion, vi devos serĉi solvon en la Interreto. Estas nenio rekte pri rusaj familiaj nomoj, sed estas demandoj pri aliaj ordigaj strangaĵoj. Ekzemple, jen problemo: unix sort traktas '-' (streketo) signoj kiel nevideblaj. Resume, la kordoj "a-b", "aa", "ac" estas ordigitaj kiel "aa", "a-b", "ac".

La respondo estas norma ĉie: uzu la programilon "C" kaj vi estos feliĉa. Ni provu:

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

Io ŝanĝiĝis. La Ivanov-oj viciĝis en la ĝusta ordo, kvankam Yolkina glitis ien. Ni revenu al la originala problemo:

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

Ĝi funkciis sen eraroj, kiel interreto promesis. Kaj ĉi tio malgraŭ Yolkina en la unua linio.

La problemo ŝajnas esti solvita, sed ĉiaokaze, ni provu alian rusan kodigon - Vindozo CP1251:

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

La ordiga rezulto, sufiĉe strange, koincidos kun la lokaĵo "C", kaj la tuta ekzemplo, sekve, funkcias sen eraroj. Ia mistikismo.

Mi ne ŝatas mistikecon en programado ĉar ĝi kutime maskas erarojn. Ni devos serioze rigardi kiel ĝi funkcias. varo kaj kion ĝi influas? LC_COLLATE .

Fine mi provos respondi la demandojn:

  • kial inaj familinomoj estis malĝuste ordigitaj?
  • kial LANG=ru_RU.CP1251 montriĝis ekvivalenta LANG=C
  • kial fari varo и aliĝi malsamaj ideoj pri la ordo de ordigitaj kordoj
  • kial estas eraroj en ĉiuj miaj ekzemploj?
  • fine kiel ordigi ŝnurojn laŭplaĉe

Ordigo en Unikodo

La unua halto estos teknika raporto n-ro 10 titolita Unikoda kunliga algoritmo Surreta unicode.org. La raporto enhavas multajn teknikajn detalojn, do mi donu mallongan resumon de la ĉefaj ideoj.

colación — "kompari" ŝnurojn estas la bazo de iu ajn ordiga algoritmo. La algoritmoj mem povas malsami ("veziko", "kunfandi", "rapide"), sed ili ĉiuj uzos komparon de paro de ŝnuroj por determini la ordon en kiu ili aperas.

Ordigi kordojn en natura lingvo estas sufiĉe kompleksa problemo. Eĉ en la plej simplaj unubajtaj kodigoj, la ordo de literoj en la alfabeto, eĉ iel diferenca de la angla latina alfabeto, ne plu koincidos kun la ordo de la nombraj valoroj, per kiuj ĉi tiuj literoj estas kodigitaj. Do en la germana alfabeto la litero Ö staras inter О и P, kaj en la kodigo CP850 ŝi interiĝas ÿ и Ü.

Vi povas provi abstrakti de specifa kodigo kaj konsideri "idealajn" literojn kiuj estas aranĝitaj en iu ordo, kiel estas farita en Unikodo. Kodigoj UTF8, UTF16 aŭ unu-bajto KOI8-R (se necesas limigita subaro de Unikodo) donos malsamajn numerajn prezentojn de literoj, sed rilatas al la samaj elementoj de la baza tabelo.

Rezultas, ke eĉ se ni konstruas simbolan tabelon de nulo, ni ne povos asigni al ĝi universalan simbolan ordon. En malsamaj naciaj alfabetoj kiuj uzas la samajn literojn, la ordo de tiuj literoj povas malsami. Ekzemple, en la franca Æ estos konsiderata ligaturo kaj ordigita kiel ŝnuro AE. En la norvega Æ estos aparta litero, kiu troviĝas post Z. Cetere, krom ligiloj kiel Æ Estas leteroj skribitaj kun pluraj simboloj. Do en la ĉeĥa alfabeto estas litero Ch, kiu staras inter H и I.

Krom diferencoj en alfabetoj, ekzistas aliaj naciaj tradicioj kiuj influas ordigon. Precipe aperas la demando: en kia ordo aperu en la vortaro vortoj konsistantaj el majusklaj kaj minusklaj literoj? Ordigo ankaŭ povas esti tuŝita de la uzo de interpunkciaj signoj. En la hispana, inversa demandosigno estas uzata komence de demanda frazo (Ĉu vi ŝatas muzikon?). En ĉi tiu kazo, estas evidente, ke demandaj frazoj ne estu grupigitaj en apartan areton ekster la alfabeto, sed kiel ordigi liniojn kun aliaj interpunkcioj?

Mi ne detenos pri ordigo de ŝnuroj en lingvoj tre malsamaj ol eŭropaj. Notu, ke en lingvoj kun skribdirekto dekstre-maldekstren aŭ supre-malsupre, signoj en linioj estas plej verŝajne konservitaj en legado, kaj eĉ ne-alfabetaj skribsistemoj havas siajn proprajn manierojn ordigi liniojn signo post signo. . Ekzemple, hieroglifoj povas esti ordigitaj laŭ stilo (Ŝlosiloj de ĉinaj signoj) aŭ per prononco. Verdire, mi tute ne scias, kiel oni devas aranĝi emojis, sed ankaŭ vi povas elpensi ion por ili.

Surbaze de la funkcioj listigitaj supre, la bazaj postuloj por kompari ŝnurojn bazitajn sur Unikodaj tabeloj estis formulitaj:

  • komparo de ĉenoj ne dependas de la pozicio de signoj en la kodtabelo;
  • sekvencoj de signoj formantaj ununuran karakteron estas reduktitaj al kanona formo (A + la supra cirklo estas la sama kiel Å);
  • Kiam oni komparas ĉenojn, signo estas konsiderata en la kunteksto de la ĉeno kaj, se necese, kombinita kun ĝiaj najbaroj en unu komparunuon (Ch en la ĉeĥa) aŭ estas dividita en plurajn (Æ franclingve);
  • ĉiuj naciaj trajtoj (alfabeto, majuskla/minusklo, interpunkcio, ordo de skribtipoj) devas esti agordita ĝis la manlibro de la ordo (emoji);
  • komparo gravas ne nur por ordigo, sed ankaŭ en multaj aliaj lokoj, ekzemple por specifi vico-intervalojn (anstataŭigante {A... z} en bash);
  • la komparo faru sufiĉe rapide.

Krome, la verkintoj de la raporto formulis komparpropraĵojn, kiujn algoritmoprogramistoj ne devus fidi:

  • la kompara algoritmo ne devus postuli apartan aron da signoj por ĉiu lingvo (rusa kaj ukrainia lingvoj kunhavas la plej multajn cirilaj signoj);
  • la komparo ne dependu de la ordo de signoj en Unikodaj tabeloj;
  • ŝnuropezo ne devus esti atributo de la ŝnuro, ĉar la sama ŝnuro en malsamaj kulturaj kuntekstoj povas havi malsamajn pezojn;
  • vicaj pezoj povas ŝanĝiĝi dum kunfandado aŭ disigo (de x < y tio ne sekvas xz < yz);
  • malsamaj ŝnuroj havantaj la samajn pezojn estas konsiderataj egalaj de la vidpunkto de la ordiga algoritmo. Enkonduko de plia ordigo de tiaj ŝnuroj eblas, sed ĝi povas malbonigi rendimenton;
  • Dum ripeta ordigo, vicoj kun la samaj pezoj povas esti interŝanĝitaj. Fortikeco estas eco de specifa ordiga algoritmo, kaj ne eco de korda kompara algoritmo (vidu la antaŭan alineon);
  • Ordigaj reguloj povas ŝanĝiĝi laŭlonge de la tempo kiam kulturaj tradicioj rafinas/ŝanĝas.

Estas ankaŭ kondiĉite ke la kompara algoritmo scias nenion pri la semantiko de la ĉenoj prilaboritaj. Tiel, ĉenoj konsistantaj nur el ciferoj ne devus esti komparitaj kiel nombroj, kaj en listoj de anglaj nomoj la artikolo (Beatles, La).

Por kontentigi ĉiujn specifitajn postulojn, estas proponita plurnivela (fakte kvarnivela) tabela ordiga algoritmo.

Antaŭe, la signoj en la ĉeno estas reduktitaj al kanona formo kaj grupigitaj en komparunuojn. Ĉiu kompara unuo ricevas plurajn pezojn respondajn al pluraj niveloj de komparo. La pezoj de komparunuoj estas elementoj de ordigitaj aroj (ĉi-kaze, entjeroj) kiuj povas esti komparitaj por pli aŭ malpli. Speciala signifo IGNORITAS (0x0) signifas, ke ĉe la responda kompara nivelo ĉi tiu unuo ne estas implikita en la komparo. La komparo de ŝnuroj povas esti ripetita plurajn fojojn, uzante la pezojn de la respondaj niveloj. Je ĉiu nivelo, la pezoj de la komparunuoj de du vicoj estas sinsekve komparitaj unu kun la alia.

En malsamaj efektivigoj de la algoritmo por malsamaj naciaj tradicioj, la valoroj de la koeficientoj povas malsami, sed la Unikoda normo inkluzivas bazan tabelon de pezoj - "Defaŭlta Unikoda Kunliga Elementa Tabelo" (DUKETO). Mi ŝatus noti, ke agordo de la variablo LC_COLLATE estas fakte indiko de la elekto de la peztabelo en la korda kompara funkcio.

Pesadkoeficientoj DUKETO aranĝite jene:

  • ĉe la unua nivelo, ĉiuj literoj estas reduktitaj al la sama kazo, diakritaj signoj estas forĵetitaj, interpunkcioj (ne ĉiuj) estas ignorataj;
  • ĉe la dua nivelo, nur diakritaj signoj estas konsiderataj;
  • ĉe la tria nivelo, nur kazo estas konsiderata;
  • ĉe la kvara nivelo, nur interpunkciaj signoj estas konsiderataj.

La komparo okazas en pluraj paŝoj: unue oni komparas la koeficientojn de la unua nivelo; se la pezoj koincidas, tiam ripeta komparo kun la duanivelaj pezoj estas efektivigita; tiam eble triono kaj kvarono.

La komparo finiĝas kiam la vicoj enhavas kongruajn unuojn de komparo kun malsamaj pezoj. Vicoj kiuj havas egalajn pezojn ĉe ĉiuj kvar niveloj estas konsideritaj egalaj unu al la alia.

Ĉi tiu algoritmo (kun amaso da pliaj teknikaj detaloj) donis la nomon al raporto n-ro 10 - "Unikoda Kunigo-Algoritmo" (ACU).

Jen kie la ordiga konduto de nia ekzemplo fariĝas iom pli klara. Estus bone kompari ĝin kun la Unikoda normo.

Por testi efektivigojn ACU estas speciala la testo, uzante dosiero de pezoj, efektiviganta DUKETO. Vi povas trovi ĉiajn amuzajn aferojn en la pesilo-dosiero. Ekzemple, ekzistas la ordo de mahjong kaj eŭropaj domenoj, same kiel la ordo de vestokompletoj en ludkartaro (simbolo 1F000 kaj plu). La kartkostumoj estas metitaj laŭ la reguloj de ponto - PCBT, kaj la kartoj en la vestokompleto estas en la sinsekvo T, 2,3, XNUMX... K.

Mane kontrolante, ke vicoj estas ĝuste ordigitaj laŭ DUKETO estus sufiĉe teda, sed, feliĉe por ni, ekzistas ekzempla efektivigo de la biblioteko por labori kun Unikodo - "Internaciaj Komponantoj por Unikodo"(ICU).

En la retejo de ĉi tiu biblioteko, disvolvita en IBM, estas demo-paĝoj, inkluzive korda kompara algoritmo paĝo. Ni eniras niajn testliniojn kun defaŭltaj agordoj kaj jen ni ricevas perfektan rusan ordigon.

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

Cetere, en la retejo ICU Vi povas trovi klarigon pri la kompara algoritmo dum prilaborado de interpunkciaj signoj. En ekzemploj Oftaj Demandoj apostrofo kaj streketo estas ignorataj.

Unikodo helpis nin, sed serĉu kialojn por stranga konduto varo в linux devos iri aliloken.

Ordigo en glibc

Rapida vido de la utilecaj fontkodoj varo el GNU Core Utils montris, ke en la utileco mem, lokalizo venas al presi la nunan valoron de la variablo LC_COLLATE kiam funkcias en sencimiga reĝimo:

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

Ŝnuraj komparoj estas faritaj uzante la norman funkcion strcoll, kio signifas, ke ĉio interesa estas en la biblioteko glibc.

En vikio la projekto glibc dediĉita al korda komparo unu alineo. El ĉi tiu alineo oni povas kompreni, ke en glibc ordigo baziĝas sur algoritmo jam konata de ni ACU (La Unikoda kunliga algoritmo) kaj/aŭ ĉe normo proksima al ĝi ISO 14651 (Internacia kordomendado kaj komparo). Koncerne la plej novan normo, oni devas rimarki, ke en la retejo standards.iso.org ISO 14651 oficiale deklarita publike disponebla, sed la responda ligilo kondukas al neekzistanta paĝo. Guglo resendas plurajn paĝojn kun ligiloj al oficialaj retejoj kiuj proponas aĉeti elektronikan kopion de la normo kontraŭ cent eŭroj, sed sur la tria aŭ kvara paĝo de serĉrezultoj estas ankaŭ rektaj ligiloj al PDF. Ĝenerale, la normo preskaŭ ne diferencas de ACU, sed estas pli enuiga por legi ĉar ĝi ne enhavas klarajn ekzemplojn de naciaj trajtoj de korda ordigo.

La plej interesa informo pri vikio estis ligilo al cimspurilo kun diskuto pri la efektivigo de korda komparo en glibc. El la diskuto oni povas lerni tion glibc uzata por kompari kordojn ISOpersona tablo La Komuna Ŝablona Tabelo (CTT), kies adreso troviĝas en la aplikaĵo A normo ISO 14651. Inter 2000 kaj 2015 ĉi tiu tabelo en glibc ne havis prizorganton kaj estis tute malsama (almenaŭ ekstere) de la nuna versio de la normo. De 2015 ĝis 2018, adapto al la nova versio de la tablo okazis, kaj nun vi havas ŝancon renkonti en la reala vivo novan version de la tablo (CentOS 8), kaj malnova (CentOS 7).

Nun kiam ni havas ĉiujn informojn pri la algoritmo kaj helpaj tabeloj, ni povas reveni al la originala problemo kaj kompreni kiel ĝuste ordigi ŝnurojn en la rusa loko.

ISO 14651 / 14652

Fontkodo de la tabelo pri kiu ni interesiĝas CTT sur la plej multaj distribuoj linux estas en la katalogo /usr/share/i18n/locales/. La tabelo mem estas en la dosiero iso14651_t1_komuna. Tiam ĉi tiu estas la dosiera direktivo kopiu iso14651_t1_common inkluzivita en la dosiero iso14651_t1, kiu, siavice, estas inkluzivita en naciaj dosieroj, inkluzive eo_US и ru_RU. Sur plej multaj distribuoj linux ĉiuj fontdosieroj estas inkluzivitaj en la baza instalado, sed se ili ne ĉeestas, vi devos instali plian pakaĵon de la distribuo.

Dosiera strukturo iso14651_t1 povas ŝajni terure multvorta, kun neevidentaj reguloj por konstrui nomojn, sed se oni rigardas ĝin, ĉio estas sufiĉe simpla. La strukturo estas priskribita en la normo ISO 14652, kies kopio estas elŝutebla de la retejo open-std.org. Alia priskribo de la dosierformato legeblas en specifoj POSIX el OpenGroup. Kiel alternativo al legado de la normo, vi povas studi la fontkodon de la funkcio kolate_legi в glibc/locale/programs/ld-collate.c.

La dosierstrukturo aspektas jene:

Defaŭlte, la signo estas uzata kiel eskapa signo, kaj la fino de la linio post la # signo estas komento. Ambaŭ simboloj povas esti redifinitaj, kio estas farita en la nova versio de la tabelo:

escape_char /
comment_char %

La dosiero enhavos ĵetonojn en la formato (kie x - deksesuma cifero). Ĉi tio estas la deksesuma prezento de Unikodaj kodpunktoj en la kodigo UCS-4 (UTF-32). Ĉiuj aliaj elementoj en angulaj krampoj (inkluzive , <2> kaj similaj) estas konsiderataj simplaj kordkonstantoj kiuj havas malmulte da signifo ekster kunteksto.

Linio LC_COLLATE diras al ni, ke poste komenciĝas la datumoj priskribantaj la komparon de ŝnuroj.

Unue, nomoj estas specifitaj por la pezoj en la kompara tabelo kaj nomoj por la simbolkombinaĵoj. Ĝenerale, la du specoj de nomoj apartenas al du malsamaj estaĵoj, sed en la fakta dosiero ili estas miksitaj. La nomoj de la pezoj estas specifitaj per la ŝlosilvorto kolating-simbolo (kompara signo) ĉar dum komparo, Unikodaj signoj kiuj havas la samajn pezojn estos konsiderataj ekvivalentaj signoj.

La totala longo de la sekcio en la nuna dosiera revizio estas proksimume 900 linioj. Mi tiris ekzemplojn el pluraj lokoj por montri la arbitrecon de nomoj kaj plurajn specojn de sintakso.

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

  • kolating-simbolo registras ŝnuron OSMANJO en la tabelo de nomoj de skvamoj
  • koliganta-simbolo .. registras sinsekvon de nomoj konsistantaj el prefikso S kaj deksesuma nombra sufikso de 1D000 por 1D35F.
  • Ffff в collating-simbol aspektas kiel granda sensigna entjero en deksesuma, sed ĝi estas nur nomo, kiu povus aspekti
  • имя signifas kodpunkton en kodigo UCS-4
  • kunliga elemento el "" registras novan nomon por paro da Unikodaj punktoj.

Post kiam la nomoj de la pezoj estas difinitaj, la faktaj pezoj estas precizigitaj. Ĉar nur pli grandaj ol-malpli rilatoj gravas kompare, la pezoj estas determinitaj per simpla sekvenco de listigitaj nomoj. La "pli malpezaj" pezoj estas listigitaj unue, poste la "pli pezaj". Mi memorigu al vi, ke al ĉiu Unikoda signo estas asignita kvar malsamaj pezoj. Ĉi tie ili estas kombinitaj en ununuran ordigitan sinsekvon. En teorio, ajna simbola nomo povus esti uzata ĉe iu ajn el la kvar niveloj, sed komentoj indikas, ke la programistoj mense apartigas nomojn en nivelojn.

% 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

Fine, la reala pezotabelo.

La sekcio de pezoj estas enfermita en ŝlosilvortoj ordon_komenco и ordon_fino. Kromaj opcioj ordon_komenco determini en kiu direkto vicoj estas skanitaj ĉe ĉiu nivelo de komparo. La defaŭlta agordo estas antaŭen. La korpo de la sekcio konsistas el linioj kiuj enhavas la simbolkodon kaj ĝiajn kvar pezojn. La signokodo povas esti reprezentita per la karaktero mem, kodpunkto, aŭ simbola nomo difinita antaŭe. Pezoj ankaŭ povas esti donitaj al simbolaj nomoj, kodpunktoj, aŭ la simboloj mem. Se kodpunktoj aŭ signoj estas uzataj, ilia pezo estas la sama kiel la nombra valoro de la kodpunkto (pozicio en la Unikoda tabelo). Signoj ne specifitaj eksplicite (kiel mi komprenas) estas konsiderataj asignitaj al la tabelo kun primara pezo kiu kongruas kun la pozicio en la Unikoda tabelo. Speciala pezvaloro IGNORITAS signifas ke la simbolo estas ignorita je la taŭga nivelo de komparo.

Por montri la strukturon de la pesilo, mi elektis tri sufiĉe evidentajn fragmentojn:

  • karakteroj kiuj estas tute ignoritaj
  • simboloj ekvivalentaj al la numero tri en la unuaj du niveloj
  • la komenco de la cirila alfabeto, kiu ne enhavas diakritajn signojn, kaj tial estas ordigita ĉefe laŭ la unua kaj tria niveloj.

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

Nun vi povas reveni al ordigo de la ekzemploj de la komenco de la artikolo. La embusko kuŝas en ĉi tiu parto de la peztabelo:

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

Videblas, ke en ĉi tiu tabelo la interpunkciaj signoj el la tabelo ASCII (inkluzive de spaco) estas preskaŭ ĉiam ignorata kiam oni komparas ĉenojn. La nuraj esceptoj estas linioj kiuj kongruas en ĉio krom interpunkciaj signoj trovitaj en kongruaj pozicioj. La linioj de mia ekzemplo (post ordigo) por la kompara algoritmo aspektas jene:

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

Konsiderante, ke en la tabelo de skaloj, majuskloj en la rusa venas post minusklaj literoj (ĉe la tria nivelo pli peza ol ), la ordigo aspektas absolute ĝusta.

Kiam oni fiksas variablon LC_COLLATE=C speciala tabelo estas ŝarĝita, kiu specifas bajton-post-bajtan komparon

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

Ĉar en Unikodo la kodpunkto Ё venas antaŭ A, la ĉenoj estas ordigitaj laŭe.

Tekstaj kaj binaraj tabeloj

Evidente, komparo de ŝnuroj estas ekstreme ofta operacio, kaj tabela analizo CTT sufiĉe multekosta proceduro. Por optimumigi aliron al la tabelo, ĝi estas kompilita en binaran formon per la komando localdef.

teamo localdef akceptas kiel parametrojn dosieron kun tabelo de naciaj karakterizaĵoj (opcio -i), en kiu ĉiuj signoj estas reprezentitaj per Unikodaj punktoj, kaj dosiero de korespondado inter Unikodaj punktoj kaj signoj de specifa kodigo (opcio -f). Kiel rezulto de la laboro, binaraj dosieroj estas kreitaj por la loko kun la nomo specifita en la lasta parametro.

glibc subtenas du binarajn dosierformatojn: "tradicia" kaj "moderna".

La tradicia formato signifas, ke la nomo de la loko estas la nomo de la subdosierujo en /usr/lib/locale/. Ĉi tiu subdosierujo konservas binarajn dosierojn LC_COLLATE, LC_CTYPE, LC_TIME kaj tiel plu. Dosiero LC_IDENTIGO enhavas la formalan nomon de la loko (kiu povas esti malsama de la dosieruja nomo) kaj komentojn.

La moderna formato implikas stoki ĉiujn lokojn en ununura arkivo /usr/lib/locale/locale-archive, kiu estas mapita al la virtuala memoro de ĉiuj procezoj uzante glibc. La loka nomo en la moderna formato estas kondiĉigita de iu kanonigo - nur ciferoj kaj literoj reduktitaj al minusklo restas en la kodaj nomoj. Do ru_RU.KOI8-R, estos konservita kiel ru_RU.koi8r.

Enigdosieroj estas serĉataj en la nuna dosierujo, same kiel en dosierujoj /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ por dosieroj CTT kaj kodi dosierojn, respektive.

Ekzemple, la komando

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

kompilos la dosieron /usr/share/i18n/locales/ru_RU uzante kodan dosieron /usr/share/i18n/charmaps/MAC-CYRILLIC.gz kaj konservu la rezulton en /usr/lib/locale/locale-archive sub la nomo ru_RU.maccyrillic

Se vi fiksas la variablon LANG = en_US.UTF-8 tiam glibc serĉos lokajn binarojn en la sekva sinsekvo de dosieroj kaj dosierujoj:

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

Se loko okazas en kaj tradiciaj kaj modernaj formatoj, tiam prioritato estas donita al la moderna.

Vi povas vidi la liston de kompilitaj lokoj per la komando loka -a.

Preparante vian komparan tabelon

Nun, armita kun la scio, vi povas krei vian propran idealan komparan tabelon de kordoj. Ĉi tiu tabelo devas ĝuste kompari rusajn literojn, inkluzive de la litero Ё, kaj samtempe konsideru interpunkciojn laŭ la tabelo. ASCII.

La procezo prepari vian propran ordigan tabelon konsistas el du etapoj: redakti la peztabelon kaj kompili ĝin en binaran formon per la komando. localdef.

Por ke la kompara tabelo estu alĝustigita kun minimumaj redaktaj kostoj, en la formato ISO 14652 Sekcioj por alĝustigi la pezojn de ekzistanta tablo estas disponigitaj. La sekcio komenciĝas per ŝlosilvorto reordigi-post kaj indikante la pozicion post kiu la anstataŭigo estas farita. La sekcio finiĝas kun la linio reordigi-fino. Se necesas korekti plurajn sekciojn de la tabelo, tiam sekcio estas kreita por ĉiu tia sekcio.

Mi kopiis novajn versiojn de la dosieroj iso14651_t1_komuna и ru_RU el la deponejo glibc al mia hejma dosierujo ~/.local/share/i18n/locales/ kaj iomete redaktis la sekcion LC_COLLATE в ru_RU. Novaj versioj de dosieroj estas plene kongruaj kun mia versio glibc. Se vi volas uzi pli malnovajn versiojn de dosieroj, vi devos ŝanĝi la simbolajn nomojn kaj la lokon kie la anstataŭaĵo komenciĝas en la tabelo.

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

Fakte, estus necese ŝanĝi la kampojn en LC_IDENTIGO tiel ke ili montras al la loko ru_MY, sed en mia ekzemplo tio ne estis bezonata, ĉar mi ekskludis la arkivon de la serĉo de lokaĵoj loka-arkivo.

ke localdef laboris kun dosieroj en mia dosierujo per variablo I18NPATH Vi povas aldoni plian dosierujon por serĉi enigajn dosierojn, kaj la dosierujo por konservi binarajn dosierojn povas esti specifita kiel vojo kun oblikvoj:

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

POSIX supozas ke en LINGVO vi povas skribi absolutajn vojojn al dosierujoj kun lokadosieroj, komencante per antaŭen oblikvo, sed glibc в linux ĉiuj vojoj estas kalkulitaj el la baza dosierujo, kiu povas esti superregita per variablo LOCPATH. Post instalado LOCPATH=~/.local/lib/locale/ ĉiuj dosieroj rilataj al lokaligo estos serĉataj nur en mia dosierujo. Arkivo de lokoj kun la variablo aro LOCPATH ignorita.

Jen la decida testo:

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

Hura! Ni faris ĝin!

Falsa laboro

Mi jam respondis al la demandoj pri ordigo de ŝnuroj prezentitaj komence, sed restas ankoraŭ kelkaj demandoj pri eraroj - videblaj kaj nevideblaj.

Ni revenu al la originala problemo.

Kaj la programo varo kaj programo aliĝi uzu la samajn kordajn komparfunkciojn de glibc. Kiel okazis tio aliĝi donis ordigan eraron sur vicoj ordigitaj per la komando varo en loko eo_US.UTF-8? La respondo estas simpla: varo komparas la tutan ŝnuron, kaj aliĝi komparas nur la ŝlosilon, kiu defaŭlte estas la komenco de la ĉeno ĝis la unua blankspaco. En mia ekzemplo, tio rezultigis erarmesaĝon ĉar la ordigo de la unuaj vortoj en la linioj ne kongruis kun la ordigo de la kompletaj linioj.

Loko "C" garantias, ke en ordigitaj ĉenoj la komencaj subĉenoj ĝis la unua spaco ankaŭ estos ordigitaj, sed tio nur maskas la eraron. Eblas elekti datumojn (homoj kun la samaj familinomoj, sed malsamaj antaŭnomoj) kiuj, sen la erarmesaĝo, donus malĝustan dosieron kunfandrezulton. Se ni volas aliĝi kunfanditaj dosierlinioj per plena nomo, tiam la ĝusta maniero estus eksplicite specifi la kampan apartigilon kaj ordigi laŭ la ŝlosila kampo, kaj ne laŭ la tuta linio. En ĉi tiu kazo, la kunfando daŭrigos ĝuste kaj ne estos eraroj en iu ajn loko:

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

Sukcese ekzekutita ekzemplo en kodado CP1251 enhavas alian eraron. La fakto estas, ke en ĉiuj dissendoj konataj de mi linux pakoj mankas kompilita loko ru_RU.CP1251. Se la kompilita loko ne estas trovita, tiam varo silente uzas bitokan komparon, kio estas kion ni observis.

Cetere, estas alia malgranda eraro rilata al la nealirebleco de kompilitaj lokoj. Teamo LOCPATH=/tmp loko -a donos liston de ĉiuj lokoj en loka-arkivo, sed kun la varia aro LOCPATH por ĉiuj programoj (inkluzive de la plej multaj lokaj) ĉi tiuj lokoj ne estos disponeblaj.

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

konkludo

Se vi estas programisto, kiu kutimas pensi, ke ŝnuroj estas aro da bajtoj, tiam via elekto LC_COLLATE=C.

Se vi estas lingvisto aŭ vortara kompilisto, tiam vi pli bone kompilu en via lokado.

Se vi estas simpla uzanto, tiam vi nur bezonas alkutimiĝi al la fakto, ke la komando ls -a eligas dosierojn komencantajn per punkto miksitaj kun dosieroj komencantaj per litero, kaj Noktomeza majoro, kiu uzas siajn internajn funkciojn por ordigi nomojn, metas dosierojn komencantajn per punkto ĉe la komenco de la listo.

referencoj

Raporto n-ro 10 Unikoda kunliga algoritmo

Signaj pezoj ĉe unicode.org

ICU — efektivigo de biblioteko por labori kun Unikodo de IBM.

Ordigo de testo uzante ICU

Karaktero pezas en ISO 14651

Priskribo de la dosierformato kun skaloj ISO 14652

Diskuto pri korda komparo en glibc

fonto: www.habr.com

Aldoni komenton