Hvernig flokkun Linux flokkar strengi

Inngangur

Þetta byrjaði allt með stuttu handriti sem átti að sameina upplýsingar um heimilisfang e-mail starfsmenn fengnir af lista yfir notendur póstlista, með starfsmannastöður fengnar úr gagnagrunni starfsmannasviðs. Báðir listarnir voru fluttir út í Unicode textaskrár UTF-8 og vistað með Unix línuendum.

Innihald mail.txt

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

Innihald buhg.txt

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

Til að sameinast voru skrárnar flokkaðar með Unix skipuninni Raða og lögð fyrir inntak Unix forritsins taka þátt, sem mistókst óvænt með villu:

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

Þegar þú skoðaðir flokkunarniðurstöðuna með augunum kom í ljós að almennt er flokkunin rétt, en ef um er að ræða tilviljanir karl- og kvennafna koma kvenkyns á undan karlkyns:

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

Lítur út eins og flokkunargalli í Unicode eða eins og birtingarmynd femínisma í flokkunaralgríminu. Hið fyrra er auðvitað sennilegra.

Við skulum leggja það til hliðar í bili taka þátt og einbeita sér að Raða. Við skulum reyna að leysa vandamálið með því að nota vísindalega pota. Í fyrsta lagi skulum við breyta staðsetningu frá en_US á ru__ru. Til að flokka væri nóg að stilla umhverfisbreytuna LC_COLLATE, en við munum ekki eyða tíma í smáatriði:

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

Ekkert breyttist.

Við skulum reyna að endurkóða skrárnar í eins bæta kóðun:

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

Aftur hefur ekkert breyst.

Það er ekkert sem þú getur gert, þú verður að leita að lausn á netinu. Það er ekkert beint um rússnesk eftirnöfn, en það eru spurningar um aðra flokkunarfurðuleika. Hér er til dæmis vandamál: unix sort meðhöndlar '-' (strik) stafi sem ósýnilega. Í stuttu máli eru strengirnir "ab", "aa", "ac" flokkaðir sem "aa", "ab", "ac".

Svarið er staðlað alls staðar: notaðu forritunarstaðinn "C" og þú munt verða ánægður. Reynum:

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

Eitthvað hefur breyst. Ivanov-menn stilltu sér upp í réttri röð þó Yolkina hafi runnið einhvers staðar. Snúum okkur aftur að upprunalega vandamálinu:

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

Það virkaði án villna eins og internetið lofaði. Og þetta þrátt fyrir Yolkina í fyrstu línu.

Vandamálið virðist vera leyst, en bara ef við á, skulum prófa aðra rússneska kóðun - Windows CP1251:

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

Flokkunarniðurstaðan, einkennilega nóg, mun falla saman við staðsetningu "C", og allt dæmið, í samræmi við það, keyrir án villna. Einhvers konar dulspeki.

Mér líkar ekki dulspeki í forritun því hún felur venjulega í sér mistök. Við verðum að skoða alvarlega hvernig það virkar. Raða og hvaða áhrif hefur það? LC_COLLATE .

Í lokin mun ég reyna að svara spurningunum:

  • afhverju var kvenkyns eftirnöfnum rangt raðað?
  • hvers vegna LANG=ru_RU.CP1251 reyndist jafngilda LANG=C
  • afhverju Raða и taka þátt mismunandi hugmyndir um röð flokkaðra strengja
  • af hverju eru villur í öllum dæmunum mínum?
  • loksins hvernig á að flokka strengi að vild

Flokkun í Unicode

Fyrsta stopp verður tækniskýrsla nr. 10 sem ber yfirskriftina Unicode söfnunaralgrím á netinu unicode.org. Skýrslan inniheldur mikið af tæknilegum smáatriðum, svo ég leyfi mér að gefa stutta samantekt á helstu hugmyndum.

Samanburði — „samanburður“ á strengjum er grundvöllur hvers kyns flokkunaralgríms. Reikniritin sjálf geta verið mismunandi („kúla“, „sameinast“, „hratt“), en þau munu öll nota samanburð á strengjapari til að ákvarða í hvaða röð þau birtast.

Að flokka strengi á náttúrulegu máli er frekar flókið vandamál. Jafnvel í einföldustu einbæta kóðun, mun röð bókstafa í stafrófinu, jafnvel á einhvern hátt frábrugðin enska latneska stafrófinu, ekki lengur falla saman við röð talnagildanna sem þessir stafir eru kóðaðir með. Svo í þýska stafrófinu stafurinn Ö stendur á milli О и P, og í kóðuninni CP850 hún kemst á milli ÿ и Ü.

Þú getur reynt að draga úr ákveðinni kóðun og íhuga „tilvalið“ stafi sem er raðað í einhverja röð, eins og gert er í Unicode. Kóðanir UTF8, UTF16 eða eitt bæti KOI8-R (ef takmarkað hlutmengi af Unicode er þörf) mun gefa mismunandi tölulegar framsetningar á bókstöfum, en vísa til sömu þátta grunntöflunnar.

Það kemur í ljós að jafnvel þótt við byggjum tákntöflu frá grunni, munum við ekki geta úthlutað henni alhliða táknaröð. Í mismunandi innlendum stafrófum sem nota sömu bókstafi getur röð þessara stafa verið mismunandi. Til dæmis á frönsku Æ verður talið bindiefni og flokkað sem strengur AE. Á norsku Æ verður sérstakt bréf, sem er staðsett eftir Z. Við the vegur, auk ligatures eins Æ Það eru stafir skrifaðir með nokkrum táknum. Svo í tékkneska stafrófinu er bókstafur Ch, sem stendur á milli H и I.

Til viðbótar við stafrófsmun eru aðrar þjóðlegar hefðir sem hafa áhrif á flokkun. Einkum vaknar spurningin: í hvaða röð ættu orð sem samanstanda af hástöfum og lágstöfum að birtast í orðabókinni? Notkun greinarmerkja getur einnig haft áhrif á flokkun. Á spænsku er öfugt spurningarmerki notað í upphafi spurnarsetningar (Líkar þér Tónlist?). Í þessu tilviki er augljóst að spurnarsetningar ættu ekki að vera flokkaðar í sérstakan klasa utan stafrófsins, en hvernig á að flokka línur með öðrum greinarmerkjum?

Ég mun ekki dvelja við að flokka strengi á tungumálum sem eru mjög ólík þeim evrópskum. Athugaðu að í tungumálum með skriftarstefnu frá hægri til vinstri eða ofan til neðst eru stafir í línum líklegast geymdir í lestrarröð og jafnvel ritkerfi sem eru ekki í stafrófsröð hafa sínar eigin leiðir til að raða línum staf fyrir staf . Til dæmis er hægt að raða héroglyfum eftir stílum (Kínverskir stafir lyklar) eða með framburði. Satt að segja hef ég ekki hugmynd um hvernig emojis ætti að vera raðað, en þú getur líka fundið eitthvað fyrir þá.

Byggt á eiginleikum sem taldir eru upp hér að ofan voru grunnkröfur til að bera saman strengi byggðar á Unicode töflum mótaðar:

  • samanburður á strengjum fer ekki eftir staðsetningu stafa í kóðatöflunni;
  • raðir stafa sem mynda einn staf eru minnkaðar í kanónískt form (A + efsti hringurinn er sá sami og Å);
  • Þegar strengir eru bornir saman er litið á staf í samhengi við strenginn og, ef nauðsyn krefur, sameinuð við nágranna sína í eina samanburðareiningu (Ch á tékknesku) eða skiptist í nokkra (Æ á frönsku);
  • öll innlend einkenni (stafróf, hástafir/lástafir, greinarmerki, röð ritgerða) verður að stilla upp að handvirkri úthlutun pöntunarinnar (emoji);
  • Samanburður er ekki aðeins mikilvægur fyrir flokkun, heldur einnig á mörgum öðrum stöðum, til dæmis til að tilgreina línusvið (sem kemur í stað {A... z} í bash);
  • samanburðurinn ætti að vera nokkuð fljótur.

Að auki mótuðu höfundar skýrslunnar samanburðareiginleika sem reiknirithönnuðir ættu ekki að treysta á:

  • samanburðarreikniritið ætti ekki að krefjast sérstakt sett af stöfum fyrir hvert tungumál (rússneska og úkraínska tungumálin deila flestum kyrillískum stöfum);
  • samanburðurinn ætti ekki að treysta á röð stafa í Unicode töflum;
  • strengjaþyngd ætti ekki að vera eiginleiki strengsins, þar sem sami strengur í mismunandi menningarsamhengi getur haft mismunandi vægi;
  • línuþyngd getur breyst við sameiningu eða skiptingu (frá x < y það fylgir því ekki xz < yz);
  • mismunandi strengir með sömu þyngd eru taldir jafnir frá sjónarhóli flokkunaralgrímsins. Það er mögulegt að taka upp viðbótarröðun slíkra strengja, en það getur dregið úr frammistöðu;
  • Við endurtekna flokkun má skipta um raðir með sömu þyngd. Sterkleiki er eiginleiki ákveðins flokkunarreiknirits en ekki eiginleiki strengsamanburðarreiknirits (sjá fyrri málsgrein);
  • Flokkunarreglur geta breyst með tímanum eftir því sem menningarhefðir betrumbætast/breytast.

Einnig er kveðið á um að samanburðaralgrímið viti ekkert um merkingarfræði strenganna sem verið er að vinna úr. Þannig ætti ekki að bera saman strengi sem samanstanda af tölustöfum sem tölur og í listum yfir ensk nöfn er greinin (Bítlarnir, The).

Til að fullnægja öllum tilgreindum kröfum er lagt til fjölþrepa (reyndar fjögurra þrepa) töfluflokkunaralgrím.

Áður voru stafirnir í strengnum minnkaðir í kanónískt form og flokkaðir í samanburðareiningar. Hverri samanburðareiningu er úthlutað nokkrum vægum sem samsvara nokkrum samanburðarstigum. Vægi samanburðareininga eru þættir raðaðra menga (í þessu tilviki heiltölur) sem hægt er að bera saman fyrir meira eða minna. Sérstök merking HUNSAÐ (0x0) þýðir að á samsvarandi samanburðarstigi tekur þessi eining ekki þátt í samanburðinum. Samanburðinn á strengjum er hægt að endurtaka nokkrum sinnum með því að nota þyngd samsvarandi stiga. Á hverju stigi eru þyngd samanburðareininga tveggja raða borin saman í röð.

Í mismunandi útfærslum reikniritsins fyrir mismunandi innlendar hefðir geta gildi stuðlanna verið mismunandi, en Unicode staðallinn inniheldur grunntöflu yfir þyngd - "Sjálfgefin Unicode safneiningatafla" (DUCET). Ég vil taka fram að stilling breyt LC_COLLATE er í raun vísbending um val á þyngdartöflu í strengjasamanburðarfallinu.

Vigtunarstuðlar DUCET raðað sem hér segir:

  • á fyrsta stigi eru allir stafir minnkaðir í sama hástafi, stafrænum orðum er hent, greinarmerki (ekki öll) eru hunsuð;
  • á öðru stigi er aðeins tekið tillit til stafsetningar;
  • á þriðja stigi er aðeins tekið tillit til tilvika;
  • á fjórða stigi er aðeins tekið tillit til greinarmerkja.

Samanburðurinn fer fram í nokkrum lotum: í fyrsta lagi eru stuðlar fyrsta þreps bornir saman; ef lóðin falla saman, þá er endurtekinn samanburður við annað stigs lóð framkvæmt; þá kannski þriðji og fjórði.

Samanburðurinn endar þegar línurnar innihalda samsvarandi samanburðareiningar með mismunandi þyngd. Raðir sem hafa jafnt vægi á öllum fjórum stigum eru taldar jafnar hver annarri.

Þetta reiknirit (með fullt af tæknilegum viðbótarupplýsingum) gaf skýrslu nr. 10 nafnið - "Unicode Collation Algorithm" (ACU).

Þetta er þar sem flokkunarhegðunin frá dæminu okkar verður aðeins skýrari. Það væri gaman að bera það saman við Unicode staðalinn.

Til að prófa útfærslur ACU það er sérstakt тест, nota lóðaskrá, framkvæmd DUCET. Þú getur fundið alls konar fyndið í vogarskránni. Til dæmis er röðin á mahjong og evrópskum domino, sem og röð lita í spilastokknum (tákn 1F000 og lengra). Kortalitin eru sett í samræmi við reglur bridge - PCBT, og spilin í litnum eru í röðinni T, 2,3, XNUMX... K.

Athugaðu handvirkt að línur séu rétt flokkaðar skv DUCET væri frekar leiðinlegt, en, sem betur fer fyrir okkur, er til fyrirmyndar útfærsla á bókasafninu til að vinna með Unicode - "Alþjóðlegir íhlutir fyrir Unicode"(ICU).

Á vefsíðu þessa bókasafns, þróað í IBM, það eru kynningarsíður, þar á meðal síðu fyrir algrím til samanburðar á strengjum. Við förum inn í prófunarlínurnar okkar með sjálfgefnum stillingum og sjá, við fáum fullkomna rússneska flokkun.

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

Við the vegur, vefsíðan ICU Þú getur fundið skýringu á samanburðaralgríminu við vinnslu greinarmerkja. Í dæmunum Algengar spurningar um söfnun skammstöf og bandstrik eru hunsuð.

Unicode hjálpaði okkur, en leitaðu að ástæðum fyrir undarlegri hegðun Raða в Linux verður að fara eitthvað annað.

Raða í glibc

Fljótleg sýn á frumkóða tólanna Raða á GNU Core Utils sýndi að í veitunni sjálfri kemur staðsetning niður á því að prenta núverandi gildi breytunnar LC_COLLATE þegar keyrt er í villuleitarstillingu:

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

Samanburður strengja er gerður með því að nota staðlaða aðgerðina strcoll, sem þýðir að allt áhugavert er á bókasafninu glibc.

Á Wiki verkefnið glibc tileinkað strengjasamanburði eina málsgrein. Af þessari málsgrein má skilja að í glibc flokkun er byggð á reiknirit sem þegar er þekkt fyrir okkur ACU (Unicode söfnunaralgrímið) og/eða á staðli nálægt því ISO 14651 (Alþjóðleg strengja röðun og samanburður). Varðandi nýjasta staðalinn skal tekið fram að á síðunni standards.iso.org ISO 14651 opinberlega lýst aðgengileg, en samsvarandi hlekkur leiðir til síðu sem ekki er til. Google skilar nokkrum síðum með tenglum á opinberar síður sem bjóða upp á að kaupa rafrænt eintak af staðlinum fyrir hundrað evrur, en á þriðju eða fjórðu síðu leitarniðurstaðna eru einnig beinir tenglar á PDF. Almennt séð er staðallinn nánast ekkert frábrugðinn ACU, en er leiðinlegra aflestrar vegna þess að það inniheldur ekki skýr dæmi um landsbundin einkenni strengjaflokkunar.

Áhugaverðustu upplýsingar um Wiki það var linkur á villuspor með umfjöllun um útfærslu strengjasamanburðar í glibc. Af umræðunni má læra það glibc notað til að bera saman strengi ISOpersónulegt borð Sameiginleg sniðmáttafla (CTT), heimilisfangið sem er að finna í umsókninni A staðallinn ISO 14651. Milli 2000 og 2015 þessi tafla inn glibc var ekki með umsjónarmann og var töluvert frábrugðinn (að minnsta kosti ytra) en núverandi útgáfu staðalsins. Frá 2015 til 2018 fór fram aðlögun að nýju útgáfunni af töflunni og nú hefurðu tækifæri til að hitta í raunveruleikanum nýja útgáfu af töflunni (CentOS 8), og gamalt (CentOS 7).

Nú þegar við höfum allar upplýsingar um reiknirit og hjálpartöflur getum við farið aftur í upprunalega vandamálið og skilið hvernig á að flokka strengi rétt á rússneska staðlinum.

ISO 14651 / 14652

Frumkóði töflunnar sem við höfum áhuga á CTT á flestum dreifingum Linux er í skránni /usr/share/i18n/locales/. Taflan sjálf er í skránni iso14651_t1_algengt. Þá er þetta skráartilskipunin afrita iso14651_t1_common innifalinn í skránni iso14651_t1, sem aftur á móti er í landsskrám, þ.m.t en_US и ru__ru. Á flestum dreifingum Linux Allar frumskrár eru innifaldar í grunnuppsetningunni, en ef þær eru ekki til staðar verður þú að setja upp aukapakka úr dreifingunni.

Uppbygging skráa iso14651_t1 kann að virðast hræðilega orðrétt, með óljósum reglum um að búa til nöfn, en ef þú horfir á það er allt frekar einfalt. Uppbyggingunni er lýst í staðlinum ISO 14652, sem hægt er að hlaða niður af vefsíðunni open-std.org. Hægt er að lesa aðra lýsingu á skráarsniðinu forskriftir POSIX frá OpenGroup. Sem valkostur við að lesa staðalinn geturðu rannsakað frumkóða aðgerðarinnar safna_lesa в glibc/locale/programs/ld-collate.c.

Uppbygging skrárinnar lítur svona út:

Sjálfgefið er að stafurinn er notaður sem escape-stafur og endir línunnar á eftir # stafnum er athugasemd. Hægt er að endurskilgreina bæði táknin, sem er það sem er gert í nýju útgáfunni af töflunni:

escape_char /
comment_char %

Skráin mun innihalda tákn á sniðinu eða (Hvar x - sextánda tölustafur). Þetta er sextánskur framsetning Unicode kóðapunkta í kóðuninni UCS-4 (UTF-32). Allir aðrir þættir í hornsvigum (þar á meðal , <2> og þess háttar) eru taldir einfaldir strengjafastar sem hafa litla merkingu utan samhengis.

Lína LC_COLLATE segir okkur að næst byrja gögnin sem lýsa samanburði á strengjum.

Í fyrsta lagi eru nöfn tilgreind fyrir þyngd í samanburðartöflunni og nöfn fyrir táknasamsetningar. Almennt séð tilheyra þessar tvær tegundir nafna tveimur mismunandi aðilum, en í raunverulegu skránni er þeim blandað saman. Nöfn lóðanna eru tilgreind með lykilorðinu safna-tákn (samanburðarstafur) því við samanburð munu Unicode stafir sem hafa sömu þyngd teljast jafngildir stafir.

Heildarlengd kaflans í núverandi skráarendurskoðun er um 900 línur. Ég dró dæmi frá nokkrum stöðum til að sýna fram á geðþótta nafna og nokkrar tegundir setningafræði.

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

  • safna-tákn skráir streng OSMANYA í nafnatöflu voga
  • safna-tákn .. skráir röð nafna sem samanstendur af forskeyti S og sextáns töluviðskeyti frá 1D000 í 1D35F.
  • Ffff в safna-tákn lítur út eins og stór ótáknuð heiltala í sextándu, en það er bara nafn sem gæti litið út
  • Имя þýðir kóðapunktur í kóðun UCS-4
  • safna-þáttur frá " " skráir nýtt nafn fyrir par af Unicode punktum.

Þegar nöfn lóðanna hafa verið skilgreind eru raunverulegar lóðir tilgreindar. Þar sem aðeins meiri en minni tengsl skipta máli í samanburði eru vægin ákvörðuð af einfaldri röð listanafna. „Léttari“ lóðin eru skráð fyrst, síðan „þyngri“. Leyfðu mér að minna þig á að hverjum Unicode staf er úthlutað fjórum mismunandi þyngd. Hér eru þau sameinuð í eina röð röð. Fræðilega séð væri hægt að nota hvaða táknræna nafn sem er á hvaða fjórum stigum sem er, en athugasemdir benda til þess að forritararnir aðskilji nöfn andlega í stig.

% 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

Að lokum, raunveruleg þyngdartafla.

Þyngdarhlutinn er innifalinn í leitarorðalínum order_start и order_end. Auka valkostir order_start ákvarða í hvaða átt línur eru skannaðar á hverju samanburðarstigi. Sjálfgefin stilling er áfram. Meginmál hlutans samanstendur af línum sem innihalda táknkóðann og fjögur þyngd hans. Stafakóðann getur verið táknuð með stafnum sjálfum, kóðapunkti eða táknrænu nafni sem áður var skilgreint. Einnig er hægt að þyngja táknræn nöfn, kóðapunkta eða táknin sjálf. Ef kóðapunktar eða stafir eru notaðir er þyngd þeirra sú sama og tölugildi kóðapunktsins (staða í Unicode töflunni). Stafir sem ekki eru sérstaklega tilgreindir (eins og ég skil) eru taldir úthlutaðir í töfluna með aðalþyngd sem passar við stöðuna í Unicode töflunni. Sérstakt þyngdargildi HUNNIÐ þýðir að táknið er hunsað á viðeigandi samanburðarstigi.

Til að sýna fram á uppbyggingu vogarinnar valdi ég þrjú nokkuð augljós brot:

  • persónur sem eru algjörlega hunsaðar
  • tákn sem jafngilda tölunni þrjú á fyrstu tveimur stigunum
  • upphaf kyrillíska stafrófsins, sem inniheldur ekki stafróf, og er því flokkað aðallega eftir fyrsta og þriðja þrepi.

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

Nú er hægt að fara aftur í flokkun dæmanna frá upphafi greinarinnar. Fyrirsátið liggur í þessum hluta þyngdartöflunnar:

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

Það má sjá að í þessari töflu eru greinarmerki úr töflunni ASCII (þar á meðal bil) er næstum alltaf hunsuð þegar strengir eru bornir saman. Einu undantekningarnar eru línur sem passa í öllu nema greinarmerki sem finnast í samsvarandi stöðum. Línurnar úr dæminu mínu (eftir flokkun) fyrir samanburðaralgrímið líta svona út:

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

Í ljósi þess að í töflunni yfir kvarða koma hástafir á rússnesku á eftir lágstöfum (á þriðja stigi þyngri en ), flokkunin lítur alveg rétt út.

Þegar breytu er stillt LC_COLLATE=C sérstök tafla er hlaðin sem tilgreinir bæti-fyrir-bæta samanburð

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 sem í Unicode kemur kóðapunkturinn Ё á undan A, eru strengirnir flokkaðir í samræmi við það.

Texti og tvíundartöflur

Augljóslega er strengjasamanburður afar algeng aðgerð og töfluþáttun CTT nokkuð kostnaðarsöm málsmeðferð. Til að hámarka aðgang að töflunni er hún sett saman í tvöfalt form með skipuninni localdef.

Team localdef samþykkir skrá með töflu yfir landseinkenni sem færibreytur (valkostur -i), þar sem allir stafir eru táknaðir með Unicode punktum og samsvörunarskrá milli Unicode punkta og stafi í tiltekinni kóðun (valkostur -f). Sem afleiðing af vinnunni eru tvíundarskrár búnar til fyrir svæði með nafninu sem tilgreint er í síðustu færibreytunni.

glibc styður tvö tvöfaldur skráarsnið: „hefðbundið“ og „nútímalegt“.

Hefðbundið snið þýðir að nafn svæðisins er nafn undirmöppunnar í /usr/lib/locale/. Þessi undirskrá geymir tvöfaldar skrár LC_COLLATE, LC_CTYPE, LC_TIME og svo framvegis. Skrá LC_IDENTIFICATION inniheldur formlegt heiti svæðisins (sem gæti verið frábrugðið nafni möppunnar) og athugasemdir.

Nútímasniðið felur í sér að geyma allar staðsetningar í einu skjalasafni /usr/lib/locale/locale-archive, sem er kortlagt á sýndarminni allra ferla sem nota glibc. Staðbundið nafn á nútímasniði er háð nokkurri skráningu - aðeins tölur og stafir minnkaðir í lágstafi eru eftir í kóðunöfnunum. Svo ru_RU.KOI8-R, verður vistað sem ru_RU.koi8r.

Inntaksskrár eru leitaðar í núverandi möppu, sem og í möppum /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ fyrir skrár CTT og kóðunarskrár, í sömu röð.

Til dæmis skipunina

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

mun setja saman skrána /usr/share/i18n/locales/ru_RU með því að nota kóðun skrá /usr/share/i18n/charmaps/MAC-CYRILLIC.gz og vistaðu niðurstöðuna í /usr/lib/locale/locale-archive undir nafninu ru_RU.maccyrillic

Ef þú stillir breytuna LANG = en_US.UTF-8 то glibc mun leita að staðbundnum tvíundum í eftirfarandi röð af skrám og möppum:

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

Ef staðsetning er bæði á hefðbundnu og nútímalegu sniði, þá er það nútímalega sett í forgang.

Þú getur skoðað listann yfir samansett svæði með skipuninni staðbundin -a.

Að útbúa samanburðartöfluna þína

Nú, vopnaður þekkingunni, geturðu búið til þína eigin fullkomnu strengjasamanburðartöflu. Þessi tafla ætti að bera rétt saman rússneska stafi, þar á meðal bókstafinn Ё, og á sama tíma taka tillit til greinarmerkja í samræmi við töfluna ASCII.

Ferlið við að útbúa eigin flokkunartöflu samanstendur af tveimur stigum: að breyta þyngdartöflunni og setja hana saman í tvöfalt form með skipuninni localdef.

Til þess að samanburðartaflan sé leiðrétt með lágmarks klippikostnaði, á sniði ISO 14652 Hlutar til að stilla þyngd núverandi töflu eru veittir. Hlutinn byrjar á lykilorði endurraða-eftir og gefur til kynna stöðuna sem skiptin er framkvæmd eftir. Kaflinn endar með línunni endurröðunarlok. Ef nauðsynlegt er að leiðrétta nokkra hluta töflunnar, þá er búið til hluti fyrir hvern slíkan hluta.

Ég afritaði nýjar útgáfur af skránum iso14651_t1_algengt и ru__ru úr geymslunni glibc í heimaskrána mína ~/.local/share/i18n/locales/ og breytti hlutanum örlítið LC_COLLATE в ru__ru. Nýjar útgáfur af skrám eru fullkomlega samhæfðar útgáfunni minni glibc. Ef þú vilt nota eldri útgáfur af skrám þarftu að breyta táknrænum nöfnum og stað þar sem skiptingin byrjar í töflunni.

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

Reyndar þyrfti að breyta reitunum inn LC_IDENTIFICATION svo að þeir bendi á staðina ru_MÍN, en í mínu dæmi var þetta ekki krafist, þar sem ég útilokaði skjalasafnið frá leitinni að staðsetningum staðar-skjalasafn.

Það localdef unnið með skrár í möppunni minni í gegnum breytu I18NPATH Þú getur bætt við viðbótarskrá til að leita að inntaksskrám og möppuna til að vista tvíundarskrár er hægt að tilgreina sem slóð með skástrikum:

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

POSIX gerir ráð fyrir að í TUNGUMÁL þú getur skrifað algjörar slóðir að möppum með staðsetningarskrám, byrjað með skástrik, en glibc в Linux allar slóðir eru taldar úr grunnskránni, sem hægt er að hnekkja í gegnum breytu LOCPATH. Eftir uppsetningu LOCPATH=~/.local/lib/locale/ allar skrár sem tengjast staðfæringu verða aðeins leitaðar í möppunni minni. Geymsla yfir staðsetningar með breytusettinu LOCPATH hunsuð.

Hér er afgerandi prófið:

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

Húrra! Okkur tókst það!

Sumir villur

Ég hef þegar svarað spurningum um flokkun strengja sem settar voru fram í upphafi, en enn eru nokkrar spurningar um villur - sýnilegar og ósýnilegar.

Snúum okkur aftur að upprunalega vandamálinu.

Og prógrammið Raða og dagskrá taka þátt nota sömu strengjasamanburðaraðgerðir frá glibc. Hvernig gerðist það taka þátt gaf upp flokkunarvillu á línum sem raðað var eftir skipuninni Raða í staðbundinni en_US.UTF-8? Svarið er einfalt: Raða ber saman allan strenginn, og taka þátt ber aðeins saman lykilinn, sem er sjálfgefið upphaf strengsins upp að fyrsta hvíta bilinu. Í mínu dæmi leiddi þetta af sér villuskilaboð vegna þess að flokkun fyrstu orðanna í línunum passaði ekki við flokkun heildarlínanna.

Staður "C" tryggir að í flokkuðum strengjum verði upphafsundirstrengir upp að fyrsta bili einnig flokkaðir, en þetta dular aðeins villuna. Það er hægt að velja gögn (fólk með sömu eftirnöfn, en mismunandi fornöfn) sem án villuboðanna myndu gefa ranga skráarsamruna niðurstöðu. Ef við viljum taka þátt sameinaðar skráarlínur með fullu nafni, þá væri rétta leiðin að tilgreina sviðsskiljuna sérstaklega og raða eftir lykilreitnum, en ekki eftir allri línunni. Í þessu tilviki mun sameiningin halda áfram rétt og engar villur verða á neinum stað:

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

Tókst að framkvæma dæmi í kóðun CP1251 inniheldur aðra villu. Staðreyndin er sú að í öllum dreifingum sem ég þekki Linux pakka vantar samansett svæði ru_RU.CP1251. Ef samanlagður staðsetning finnst ekki, þá Raða notar hljóðlaust bæti-fyrir-bæta samanburð, sem er það sem við sáum.

Við the vegur, það er annar lítill galli sem tengist óaðgengilegum staðsetningum. Lið LOCPATH=/tmp staðsetning -a mun gefa lista yfir alla staði í staðar-skjalasafn, en með breytusettinu LOCPATH fyrir öll forrit (þar á meðal flest staður) þessar staðsetningar verða ekki tiltækar.

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

Ályktun

Ef þú ert forritari sem er vanur að halda að strengir séu sett af bætum, þá er þitt val LC_COLLATE=C.

Ef þú ert málvísindamaður eða orðabókaþýðandi, þá er betra að safna saman á þínu svæði.

Ef þú ert einfaldur notandi, þá þarftu bara að venjast því að skipunin ls -a gefur út skrár sem byrja á punkti í bland við skrár sem byrja á bókstaf, og Yfirmaður miðnætur, sem notar innri aðgerðir sínar til að flokka nöfn, setur skrár sem byrja á punkti í byrjun listans.

tilvísanir

Skýrsla nr. 10 Unicode söfnunaralgrím

Persónuþyngd á unicode.org

ICU — útfærsla á bókasafni til að vinna með Unicode frá IBM.

Flokkunarpróf með því að nota ICU

Persóna þyngist ISO 14651

Lýsing á skráarsniði með vogum ISO 14652

Umfjöllun um strengjasamanburð í glibc

Heimild: www.habr.com

Bæta við athugasemd