Introduktion
Det hela började med ett kort skript som skulle slÄ samman information om adresser e-post frÄn anstÀlldas lista i mailutskicket med anstÀlldas roller frÄn personalavdelningens databas. BÄda listorna exporterades till textfiler i Unicode-kodning UTF-8 och sparades med Unix-radslut.
InnehÄll mail.txt
Ivanov Andrei;ia@example.comInnehÄll buhg.txt
Ivanova Alla;mÄlare
Yelkina Ella;kranförare
Ivanov Andrei;svetsare
Abakanov Mikhail;mÄlareFör att slÄ samman var filerna sorterade med Unix-kommandot sort och matades in i programmet join, som ovÀntat avslutades med ett fel:
$> sort buhg.txt > buhg.srt
$> sort mail.txt > mail.srt
$> join buhg.srt mail.srt > result
join: buhg.srt:4: Ă€r inte sorterat: Ivanov Andrei;svetsareĂversiktsgranskning av sorteringsresultatet visade att sorteringen överlag var korrekt, men nĂ€r det gĂ€llde överlappningar mellan manliga och kvinnliga efternamn hamnar kvinnorna före mĂ€nnen:
$> sort buhg.txt
Abakanov Mikhail;mÄlare
Yelkina Ella;kranförare
Ivanova Alla;mÄlare
Ivanov Andrei;svetsareDet ser ut som en bugg i Unicode-sortering eller ett uttryck för feminism i sorteringsalgoritmen. Det första verkar förstÄs mer rimligt.
LÄt oss skjuta pÄ det för nu join och fokusera pÄ sort. LÄt oss försöka lösa uppgiften med trial and error. Först Àndrar vi locale frÄn en_US till ru_RU. För sortering skulle det ha rÀckt att sÀtta miljövariabeln LC_COLLATE, men vi ska inte snÄla:
$> LANG=ru_RU.UTF-8 sort buhg.txt
Abakanov Mikhail;mÄlare
Yelkina Ella;kranförare
Ivanova Alla;mÄlare
Ivanov Andrei;svetsareIngenting har förÀndrats.
LÄt oss försöka konvertera filerna till en enbyteskodning:
$> iconv -f UTF-8 -t KOI8-R buhg.txt
| LANG=ru_RU.KOI8-R sort
| iconv -f KOI8-R -t UTF8à terigen har ingenting förÀndrats.
Det Àr ingen utvÀg, vi mÄste söka lösningen pÄ internet. Direkt om ryska efternamn finns inget, men det finns frÄgor om andra konstigheter i sortering. Till exempel finns det ett problem: . Kort sagt sorteras strÀngarna "a-b", "aa", "ac" som "aa", "a-b", "ac".
Svaren Àr alltid standard: anvÀnd programmerarlocale "C" och du fÄr din lycka. Vi testar:
$> LANG=C sort buhg.txt
Yelkina Ella;kranförare
Abakanov Mikhail;mÄlare
Ivanov Andrei;svetsare
Ivanova Alla;advokatNÄgot har förÀndrats. Ivanov-familjen har stÀllt sig i rÀtt ordning, men Jelkin har glidit bort. Vi ÄtergÄr till den ursprungliga uppgiften:
$> LANG=C sort buhg.txt > buhg.srt
$> LANG=C sort mail.txt > mail.srt
$> LANG=C join buhg.srt mail.srt > resultDet fungerade utan fel, precis som internet lovade. Och detta trots Jelkin i den första raden.
Problemet verkar vara löst, men för sĂ€kerhets skull provar vi en till rysk kodning â Windows-kodningen. CP1251:
$> iconv -f UTF-8 -t CP1251 buhg.txt
| LANG=ru_RU.CP1251 sort
| iconv -f CP1251 -t UTF8 Sorteringsresultatet, mÀrkligt nog, kommer att överensstÀmma med lokaliteten. "C", och hela exemplet gÄr dÀrmed igenom utan fel. Det Àr nÄgon slags mystik.
Jag gillar inte mystik i programmering, eftersom den vanligtvis döljer fel. Jag mÄste verkligen göra en djupdykning i hur det fungerar. sort och vad det pÄverkar. LC_COLLATE .
Till slut kommer jag att försöka svara pÄ frÄgorna:
- varför kvinnliga efternamn sorterade felaktigt.
- varför LANG=ru_RU.CP1251 visade sig vara ekvivalent. LANG=C
- varför fÄr sort och join olika representationer av ordningen för sorterade rader.
- varför finns det fel i alla mina exempel.
- slutligen, hur man sorterar rader efter eget tycke.
Sortering i Unicode.
Den första stopppunkten kommer att vara teknisk rapport nr 10 med titeln. pÄ webbplatsen Rapporten innehÄller mÄnga tekniska detaljer, sÄ jag tillÄter mig att ge en sammanfattning av huvuddiskussionerna.
Collation â "jĂ€mförelse" av strĂ€ngar â Ă€r grunden för varje sorteringsalgoritm. SjĂ€lva algoritmerna kan variera ("bubblande", "sammanfogande", "snabb"), men alla kommer att anvĂ€nda jĂ€mförelse av strĂ€ngpar för att bestĂ€mma deras ordning.
Sortering av strĂ€ngar pĂ„ naturligt sprĂ„k Ă€r ett ganska komplext problem. Ăven i de enklaste enbyte-kodningarna kan ordningen pĂ„ bokstĂ€ver i alfabetet, Ă€ven i nĂ„got som avviker frĂ„n den engelska latinska uppsĂ€ttningen, redan skilja sig frĂ„n den ordning av numeriska vĂ€rden som dessa bokstĂ€ver koden. SĂ„ i det tyska alfabetet kommer bokstaven Ă att ligga mellan Till och P, men i kodningen CP850 hamnar den mellan Ăż och Ă..
Man kan försöka abstrahera frÄn den specifika kodningen och betrakta "idealiska" bokstÀver, som Àr placerade i en viss ordning, som det görs i Unicode. Kodningar UTF8, UTF16 eller enbyte-kodning KOI8-R (om ett begrÀnsat delmÀngd av Unicode behövs) kommer att ge olika numeriska representationer av bokstÀverna, men hÀnvisa till samma grundlÀggande tabell.
Det visar sig att Àven om vi bygger en teckentabell frÄn grunden, kommer vi inte att kunna tilldela en universell ordning för tecknen. I olika nationella alfabet som anvÀnder samma bokstÀver kan ordningen av dessa bokstÀver skilja sig. Till exempel pÄ franska à kommer att betraktas som en ligatur och sorteras som en strÀng AE. I norska sprÄket à kommer att vara en separat bokstav som ligger efter Z. För övrigt, förutom ligaturer av typen à finns det bokstÀver som skrivs med flera tecken. SÄ i det tjeckiska alfabetet finns bokstaven Ch, som ligger mellan H och I.
Förutom skillnader i alfabeten finns det Ă€ven andra nationella traditioner som pĂ„verkar sorteringen. SĂ€rskilt uppstĂ„r frĂ„gan: i vilken ordning bör ord bestĂ„ende av versaler och gemener följa i ordboken? Ăven punktering kan pĂ„verka sorteringen. PĂ„ spanska sĂ€tts en omvĂ€nd frĂ„getecken i början av en frĂ„ga (ÂżTe gusta la mĂșsica?). I detta fall Ă€r det uppenbart att frĂ„gande meningar inte bör grupperas i en egen kluster utanför alfabetet, men hur bör man sortera strĂ€ngar med andra skiljetecken?
Jag kommer inte att gÄ in pÄ sorteringen av strÀngar i sprÄk som skiljer sig kraftigt frÄn europeiska. Jag nÀmner att i sprÄk med skrivriktningar frÄn höger till vÀnster eller ovanifrÄn och ner, lagras tecknen i strÀngar förmodligen i lÀsordning, och Àven i icke-alphabetiska skriftsystem finns det sina egna sÀtten att ordna tecken för tecken. Till exempel kan hieroglyfer ordnas efter skrivsÀtt () eller efter uttal. Hur emoticons bör ordnas, kan jag Àrligt talat inte förestÀlla mig, men Àven för dem kan nÄgot utarbetas.
Baserat pÄ ovanstÄende egenskaper har de grundlÀggande kraven för jÀmförelse av strÀngar baserade pÄ Unicode-tabeller formulerats:
- strÀngjÀmförelser Àr oberoende av tecknens position i kodtabellen;
- sekvenser av tecken som bildar ett enskilt tecken omvandlas till kanonisk form (A + den övre cirkeln Àr detsamma som à );
- vid strÀngjÀmförelse betraktas tecken i kontexten av strÀngen och, om det Àr nödvÀndigt, kombineras med grannarna till en enhet för jÀmförelse (Ch i tjeckiska) eller delas upp i flera (à pÄ franska);
- Alla nationella egenskaper (alfabet, versaler/gemener, skiljetecken, skriftsprÄkets ordning) bör kunna anpassas, Ànda ner till manuell tilldelning av ordning (emojier);
- JĂ€mförelse Ă€r viktigt inte bara för sortering utan ocksĂ„ pĂ„ mĂ„nga andra stĂ€llen, till exempel för att ange radrĂ€ckor (ersĂ€ttning {A⊠Ń} i bash);
- JÀmförelsen bör utföras tillrÀckligt snabbt.
Dessutom har författarna till rapporten formulerat egenskaper för jÀmförelse som algoritmutvecklare inte bör förlita sig pÄ:
- JÀmförelsealgoritmen bör inte krÀva en separat uppsÀttning tecken för varje sprÄk (ryska och ukrainska anvÀnder gemensamt majoriteten av kyrilliska tecken);
- JÀmförelse bör inte bygga pÄ tecknens ordning i Unicode-tabeller;
- StrÀngens vikt ska inte vara en egenskap hos strÀngen, eftersom samma strÀng i olika kulturella sammanhang kan ha olika vikter;
- Vikterna för strÀngar kan förÀndras vid sammanslagning eller delning (frÄn x < y Det betyder inte att xz < yz);
- Olika strÀngar som har samma vikt betraktas som lika frÄn algoritmens sorteringssynpunkt. Att införa ytterligare ordning för sÄdana strÀngar Àr möjligt, men det kan försÀmra prestandan;
- Vid upprepade sorteringar kan strÀngar med samma vikt byta plats. Stabilitet Àr en egenskap hos en specifik sorteringsalgoritm, inte en egenskap hos en strÀngjÀmförelsealgoritm (se föregÄende punkt);
- Sorteringsregler kan förÀndras över tid i takt med att kulturella traditioner förtydligas/förÀndras.
Det konstateras ocksÄ att jÀmförelsealgoritmen inte kÀnner till semantiken hos de bearbetade strÀngarna. SÄ, strÀngar som endast bestÄr av siffror bör inte jÀmföras som tal, och i listor över engelska namn bör artikeln inte tas bort (Beatles, The).
För att uppfylla alla angivna krav föreslÄs en flernivÄ (faktiskt fyrnivÄ) tabellsorteringsalgoritm.
I förvÀg omvandlas tecknen i strÀngen till kanonisk form och grupperas i jÀmförelseenheter. Varje jÀmförelseenhet tilldelas flera vikter som motsvarar flera nivÄer av jÀmförelse. Vikterna för jÀmförelseenheterna Àr element i ordnade mÀngder (i detta fall heltal) som kan jÀmföras som större eller mindre. Speciellt vÀrde IGNORED (0x0) innebÀr att enheten i frÄga inte deltar i jÀmförelsen pÄ den aktuella nivÄ som jÀmförs. StrÀngjÀmförelser kan upprepas flera gÄnger med hjÀlp av vikter för de aktuella nivÄerna. Vid varje nivÄ jÀmförs vikterna för jÀmförelseenheterna i de tvÄ strÀngarna med varandra.
I olika implementationer av algoritmen för olika nationella traditioner kan koefficientvĂ€rdena skilja sig, men standarden Unicode innehĂ„ller en grundlĂ€ggande vikt-tabell â "Standard Unicode Collation Element Table" (DUCET). Jag vill pĂ„peka att installationen av variabeln LC_COLLATE faktiskt Ă€r en indikation pĂ„ valet av vikt-tabellen i strĂ€ngjĂ€mförelsefunktionen.
Viktkoefficienterna DUCET Àr ordnade pÄ följande sÀtt:
- pÄ första nivÄn konverteras alla bokstÀver till samma fall, diakritiska tecken avvisas, och skiljetecken (inte alla) ignoreras;
- pÄ andra nivÄn beaktas endast diakritiska tecken;
- pÄ tredje nivÄn beaktas endast fallet;
- pÄ fjÀrde nivÄn beaktas endast skiljetecken.
JÀmförelsen sker i flera genomgÄngar: först jÀmförs koefficienterna pÄ första nivÄn; om vikterna stÀmmer överens, sker en upprepning av jÀmförelsen med vikterna pÄ andra nivÄn; följt av kanske tredje och fjÀrde.
JÀmförelsen avslutas nÀr det finns motsvarande jÀmförelseenheter med olika vikter i strÀngerna. StrÀngar med lika vikter pÄ alla fyra nivÄerna anses vara lika.
Denna algoritm (med massor av ytterligare tekniska detaljer) gav namn Ă„t rapport nr 10 â "Unicode Collation Algorithm" (UCA).
Vid denna punkt blir sorteringsbeteendet i vÄrt exempel nÄgot mer begripligt. Det vore bra att jÀmföra det med standarden Unicode.
För att testa implementationerna UCA finns det ett speciellt , som anvĂ€nder , implementerar DUCET. I vikt-filen kan man hitta olika roliga detaljer. Till exempel finns dĂ€r ordningen för mahjongbrickor och europeiskt domino, samt ordningen för fĂ€rger i en kortlek (symbol 1F000 och sĂ„ vidare). KortfĂ€rgerna Ă€r placerade enligt bridge-reglerna â PCHBT, och korten i varje fĂ€rg â i ordningen T,2,3⊠K.
Manuell kontroll av korrektheten i sorteringen av strĂ€ngar enligt DUCET skulle vara ganska uttröttande, men lyckligtvis för oss finns det en exemplariskt genomförd implementation av biblioteket för att arbeta med Unicode â "" (ICU).
PÄ webbplatsen för detta bibliotek, utvecklat av IBM, finns demonstrationssidor, inklusive . Vi matar in vÄra teststrÀngar med standardinstÀllningar och, Äh under, fÄr en perfekt rysk sortering.
Abakanov Michail;mÄlare
Jolkina Ella;kranförare
Ivanov Andrej;svetsare
Ivanova Alla;advokatFörresten, pÄ webbplatsen ICU kan man hitta en förtydligande av hur algoritmen för jÀmförelse fungerar vid hantering av skiljetecken. I exempel ignoreras apostrof och bindestreck.
Unicode hjÀlpte oss, men att leta efter orsakerna till det konstiga beteendet sort pÄ Linux kommer vi att behöva göra nÄgon annanstans.
Sortering i glibc
En snabb titt pÄ kÀllkoderna för verktyget sort frÄn GNU Core Utils visade att lokalisering i sjÀlva verktyget handlar om att skriva ut det nuvarande vÀrdet av variabeln LC_COLLATE vid körning i felsökningslÀge:
$ sort --debug buhg.txt > buhg.srt
sort: anvÀnder 'en_US.UTF8' sorteringsreglerStrÀngjÀmförelse görs av den standardiserade funktionen strcoll, vilket innebÀr att allt intressant finns i biblioteket glibc.
PĂ„ wiki projektet glibc strĂ€ngjĂ€mförelse Ă€gnas . Av detta stycke kan man förstĂ„ att i glibc sorteringen baseras pĂ„ den redan kĂ€nda algoritmen UCA (The Unicode collation algorithm) och/eller den nĂ€rstĂ„ende standarden ISO 14651 (Internationell strĂ€ngordning och -jĂ€mförelse). AngĂ„ende den senaste standarden bör nĂ€mnas att den pĂ„ webbplatsen ISO 14651 officiellt har utlysts offentlig, men den tillhörande lĂ€nken leder till en icke-existerande sida. Google visar flera sidor med lĂ€nkar till officiella webbplatser som erbjuder att köpa en elektronisk kopia av standarden för hundra euro, men pĂ„ den tredje-fjĂ€rde sidan av sökresultatet finns Ă€ven direkta lĂ€nkar till PDF. Ăver lag skiljer sig standarden praktiskt taget inte frĂ„n UCA, men den Ă€r trĂ„kigare att lĂ€sa eftersom den inte innehĂ„ller tydliga exempel pĂ„ nationella sĂ€rdrag i strĂ€ngsortering.
Den mest intressanta informationen pÄ wiki var en lÀnk till som diskuterar implementeringen av strÀngjÀmförelse i glibc. FrÄn diskussionen kan man förstÄ att i glibc för strÀngjÀmförelse anvÀnds ISOett tabellformat (CTT), vars adress kan hittas i bilagan A till standarden ISO 14651. Mellan 2000 och 2015 var denna tabell i glibc hade ingen förvaltare och skilde sig ganska mycket (Ätminstone utseendemÀssigt) frÄn den aktuella standardversionen. FrÄn 2015 till 2018 genomgick den anpassning till den nya tabellen och just nu har du en chans att möta bÄde den nya tabellen (CentOS 8), samt den gamla (CentOS 7).
Nu nÀr vi har all information om algoritmen och hjÀlp-tabellerna kan vi ÄtervÀnda till det ursprungliga problemet och förstÄ hur man korrekt sorterar strÀngar i rysk lokal.
ISO 14651/14652
KÀllkoden för den tabell vi Àr intresserade av CTT finns i de flesta distributioner Linux i katalogen /usr/share/i18n/locales/. SjÀlva tabellen finns i filen iso14651_t1_common. Sedan inkluderas denna fil med direktivet copy iso14651_t1_common i filen iso14651_t1, som i sin tur inkluderas i nationella filer, inklusive en_US och ru_RU. I de flesta distributioner Linux ingÄr alla kÀllfiler i den grundlÀggande installationen, men om de inte finns, mÄste du installera ett extra paket frÄn distributionen.
Strukturen pÄ filen iso14651_t1 kan tyckas vara fruktansvÀrt ordig, med oklara regler för namnens uppbyggnad, men om man begriper sig pÄ det Àr allt ganska enkelt. Strukturen beskrivs i standarden ISO 14652, en kopia av vilken kan laddas ner frÄn sidan . En annan beskrivning av filformatet kan lÀsas i POSIX frÄn OpenGroup. Som ett alternativ till att lÀsa standarden kan man studera kÀllkoden för funktionen collate_read pÄ glibc/locale/programs/ld-collate.c.
Filens struktur ser ut som följer:
Som standard anvÀnds symbolen som en escape-symbol, och slutet av en rad efter symbolen # Àr en kommentar. BÄda symbolerna kan ÄsidosÀttas, vilket har gjorts i den nya versionen av tabellen:
escape_char /
comment_char %I filen förekommer det token i formatet <Uxxxx> eller <Uxxxxxxxx> (dĂ€r x â Ă€r en hexadecimalt siffra). Detta Ă€r den hexadecimala representationen av Unicode-kodpunkter i kodningen UCS-4 (UTF-32). Alla andra element i vinkelparenteser (inklusive <Uxxxx_xxxx>, <2> och liknande), betraktas som vanliga strĂ€ngkonstanter som saknar sĂ€rskild betydelse utanför kontext.
StrÀng LC_COLLATE berÀttar för oss att datan som beskriver strÀngjÀmförelserna börjar hÀr.
Först anges namn för vikterna i jÀmförelse tabellen och namn för kombinationerna av tecken. Generellt tillhör de tvÄ namntyperna tvÄ olika entiteter, men i den verkliga filen Àr de blandade. Namnen pÄ vikterna anges med nyckelordet sorteringssymbol (sorteringssymbol), eftersom Unicode-symboler med samma vikt kommer att betraktas som jÀmförbara symboler.
Den totala lÀngden pÄ sektionen i den nuvarande versionen av filen Àr cirka 900 rader. Jag har tagit exempel frÄn flera stÀllen för att visa slumpmÀssigheten i namnen och nÄgra typer av syntax.
LC_COLLATE
sorteringssymbol
sorteringssymbol
sorteringssymbol
sorteringssymbol
...
sorteringssymbol
sorteringssymbol
sorteringssymbol
...
sorteringssymbol ..
sorteringssymbol % Garanterat största symbolvÀrdet. BehÄll i slutet av denna lista
...
sorteringselement frÄn ""
sorteringselement frÄn ""- sorteringssymbol registrerar strÀng OSMANYA i viktens namn tabell
- sorteringssymbol .. registrerar en sekvens av namn som bestÄr av ett prefix S och ett hexadecimalt nummer suffix frÄn 1D000 till 1D35F.
- FFFF pÄ sorteringssymbol ser ut som ett stort osignerat heltal i hexadecimalt format, men <SFFFF> Àr bara ett namn som skulle kunna se ut som <VERYBIGVAL>
- namn <U0413> betyder kodpunkt i kodningen UCS-4
- sorteringselement frÄn "" registrerar ett nytt namn för paret av Unicode-punkter.
NÀr vikterna har definierats sÀtts de faktiskt vikterna. Eftersom endast förhÄllandet mellan större och mindre spelar roll i jÀmförelsen, bestÀms vikterna av en enkel sekvens av namn. De "lÀttare" vikterna listas först, följt av de "tyngre". Jag pÄminner om att varje Unicode-symbol tilldelas fyra olika vikter. HÀr sammanstÀlls de i en enhetlig ordnad sekvens. Teoretiskt kan vilket symboliskt namn som helst anvÀndas pÄ vilket av de fyra nivÄerna som helst, men kommentarer visar att utvecklarna mentalt uppdelar namnen efter nivÄer.
% Symboliska viktfördelningar
% Tredje nivÄ viktfördelningar
...
% Andra nivÄ viktfördelningar
% KOMBINERANDE LĂ
G LINJE
% KOMBINERANDE KOMMA OCH ĂVER
% KOMBINERANDE Ă
TERVĂND KOMMA OCH ĂVER
...
% Första nivÄ viktfördelningar
% HORIZONTAL TABULATION
% LINJEBREAK
% VERTIKAL TABULERING
...
% CYRILLIC LITTLE LETTER DE
% CYRILLIC LITTLE LETTER KOMI DE
% CYRILLIC LITTLE LETTER DJE
% CYRILLIC LITTLE LETTER KOMI DJE
% CYRILLIC LITTLE LETTER GJE
% CYRILLIC LITTLE LETTER ZE WITH DESCENDER
% CYRILLIC LITTLE LETTER IE
% CYRILLIC LITTLE LETTER IE WITH BREVE
% CYRILLIC LITTLE LETTER UKRAINIAN IE
% CYRILLIC LITTLE LETTER ZHESlutligen, sjÀlva vikt-tabellen.
Viktsektionen Àr innesluten i rader med nyckelord order_start och order_end. Ytterligare parametrar order_start bestÀmmer i vilken riktning raderna visas pÄ varje jÀmförelse nivÄ. Som standard anvÀnds parametern framÄt. Kroppen av sektionen bestÄr av rader som innehÄller teckenkod och fyra av dess vikter. Teckenkoden kan representeras av sjÀlva tecknet, kodpunkten eller det symboliska namnet som definierats tidigare. Vikterna kan ocksÄ anges med symboliska namn, kodpunkter eller sjÀlva tecknen. Om kodpunkter eller tecken anvÀnds, kommer deras vikt att motsvara det numeriska vÀrdet av kodpunkten (positionen i Unicode-tabellen). Tecken som inte anges tydligt (sÄvitt jag förstÄr) anses tilldelade i tabellen med en primÀr vikt motsvarande positionen i Unicode-tabellen. Det speciella viktvÀrdet IGNORE betyder att detta tecken ignoreras pÄ den aktuella jÀmförelsenivÄn.
För att demonstrera vikternas struktur har jag valt tre ganska uppenbara fragment:
- tecken som helt ignoreras
- tecken som Àr likvÀrdiga med siffran tre pÄ de första tvÄ nivÄerna
- början av det kyrilliska alfabetet, som inte innehÄller diakritiska tecken, och dÀrför sorteras frÀmst efter första och tredje nivÄerna.
order_start forward;forward;forward;forward,position
IGNORE;IGNORE;IGNORE;IGNORE % NULL (in 6429)
IGNORE;IGNORE;IGNORE;IGNORE % START OF HEADING (in 6429)
IGNORE;IGNORE;IGNORE;IGNORE % START OF TEXT (in 6429)
...
;;; % DIGIT THREE
;;; % FULLWIDTH DIGIT THREE
;;; % PARENTHESIZED DIGIT THREE
;;; % DIGIT THREE FULL STOP
;;; % MATHEMATICAL BOLD DIGIT THREE
...
;;; % CYRILLIC SMALL LETTER A
;;; % CYRILLIC CAPITAL LETTER A
;;; % CYRILLIC SMALL LETTER A WITH BREVE
;;; % CYRILLIC SMALL LETTER A WITH BREVE
...
;;; % CYRILLIC SMALL LETTER BE
;;; % CYRILLIC CAPITAL LETTER BE
;;; % CYRILLIC SMALL LETTER VE
;;; % CYRILLIC CAPITAL LETTER VE
...
order_endNu kan vi ÄtergÄ till sorteringen av exemplen frÄn början av artikeln. FÀllan ligger just i denna del av vikt-tabellen:
IGNORE;IGNORE;IGNORE; % SPACE
IGNORE;IGNORE;IGNORE; % EXCLAMATION MARK
IGNORE;IGNORE;IGNORE; % QUOTATION MARK
...Det Àr tydligt att i denna tabell Àr interpunktionstÀllen frÄn tabellen ASCII (inklusive mellanslag) vid jÀmförelse av strÀngar nÀstan alltid ignoreras. Undantaget utgörs av strÀngar som överensstÀmmer helt, förutom interpunktion som förekommer pÄ matchande positioner. StrÀngarna i mitt exempel (efter sortering) ser ut sÄ hÀr för jÀmförelsealgoritmen:
AbakanovMihailmÄlare
JolkinaEllaKranskÀr
IvanovaAllaMÄlare
IvanovAndreiSmedMed tanke pÄ att stora bokstÀver i ryska sprÄket i vikt-tabellen kommer efter smÄ bokstÀver (pÄ tredje nivÄn <CAP> tyngre Àn <MIN>), ser sorteringen helt korrekt ut.
Vid instÀlld variabel LC_COLLATE=C laddas en sÀrskild tabell som anger byte-för-byte jÀmförelse
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'
};Eftersom kodpunkten Рi Unicode stÄr före A, sorteras strÀngarna dÀrefter.
Text- och binÀra tabeller
Det Àr uppenbart att strÀngjÀmförelse Àr en mycket vanlig operation, medan tolkning av tabellen CTT Àr en relativt kostsam procedur. För att optimera Ätkomsten till tabellen kompileras den till binÀr form av kommandot localedef.
Team localedef tar emot som parametrar en fil med nationella sÀrdrag (alternativ -i), dÀr alla tecken representeras av Unicode-punkter, och en fil med överensstÀmmelse mellan Unicode-punkter och tecken i en specifik kodning (alternativ -f). Som resultat av processen skapas binÀrfiler för lokalinstÀllningen, namnet som anges i den sista parametern.
Glibc stödjer tvÄ format för binÀra filer: "traditionellt" och "modernt".
Det traditionella formatet innebÀr att lokalinstÀllningens namn Àr namnet pÄ underkatalogen i /usr/lib/locale/. I denna underkatalog lagras binÀrfiler LC_COLLATE, LC_CTYPE, LC_TIME och sÄ vidare. Filen LC_IDENTIFICATION innehÄller det formella namnet pÄ lokalinstÀllningen (som kan skilja sig frÄn katalognamnet) och kommentarer.
Det moderna formatet förutsĂ€tter att alla lokalinstĂ€llningar lagras i ett enda arkiv /usr/lib/locale/locale-archive, vilket avbildas i det virtuella minnet för alla processer som anvĂ€nder glibc. Namnet pĂ„ lokal i modern form genomgĂ„r viss kanonisering â endast siffror och bokstĂ€ver i smĂ„ bokstĂ€ver kvarstĂ„r i kodningens namn. SĂ„ ru_RU.KOI8-R, sparas som ru_RU.koi8r.
IngÄngsfiler söks i den aktuella katalogen, samt i kataloger /usr/share/i18n/locales/ och /usr/share/i18n/charmaps/ för filer CTT och kodningsfiler respektive.
Till exempel, kommandot
localedef -i ru_RU -f MAC-CYRILLIC ru_RU.MAC-CYRILLICkompilerar filen /usr/share/i18n/locales/ru_RU med hjÀlp av kodningsfilen /usr/share/i18n/charmaps/MAC-CYRILLIC.gz och sparar resultatet i /usr/lib/locale/locale-archive under namnet ru_RU.maccyrillic
Om du sÀtter variabeln LANG=en_US.UTF-8 dÄ glibc kommer den att leta efter binÀrfiler för lokal i följande ordning av filer och kataloger:
/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/Om en lokal förekommer bÄde i traditionell och modern form, ges prioritet till den moderna.
För att visa listan över kompilerade lokaler kan kommandot locale -a.
Förbereda sin egen sorteringstabell
Nu, bevĂ€pnad med kunskap, kan du skapa din egen perfekta sorteringstabell för strĂ€ngar. Denna tabell ska korrekt jĂ€mföra ryska bokstĂ€ver, inklusive bokstaven Đ, och ta hĂ€nsyn till skiljetecken enligt tabellen ASCII.
Processen för att förbereda sin egen sorteringstabell bestÄr av tvÄ steg: redigering av vikt-tabellen och kompilerande av den i binÀr form med kommandot localedef.
För att justera sorteringstabellen med minimal redigeringskostnad, finns det sektioner för justering av vikter i det befintliga formatet. ISO 14652 Sektionen börjar med nyckelordet reorder-after och anvisningen av positionen, efter vilken ersÀttningen görs. Sektionen avslutas med raden reorder-end. Om du behöver justera flera avsnitt av tabellen, skapas en sektion för varje sÄdant avsnitt.
Jag kopierade nya versioner av filerna iso14651_t1_common och ru_RU frÄn arkivet glibc till min hemkatalog ~\/ .local\/share\/i18n\/locales\/ och gjorde nÄgra smÄ Àndringar i avsnittet LC_COLLATE pÄ ru_RU. De nya versionerna av filerna Àr helt kompatibla med min version glibc. Om du vill anvÀnda Àldre versioner av filer, mÄste du Àndra de symboliska namnen och den plats dÀr ersÀttningen börjar i tabellen.
LC_COLLATE
% Kopiera mallen frÄn ISO/IEC 14651
kopiera "iso14651_t1"
ordna-efter
;;; % MELLANSPEL
;;; % UTTALSMĂRKE
;;; % CITATMĂRKE
...
;;; % HĂGER KLAMMERT
;;; % TILDE
ordna-slut
SLUT LC_COLLATEFaktiskt borde jag ha bytt ut fÀlten i LC_IDENTIFICATION sÄ att de pekade pÄ lokaliteten ru_MY, men i mitt exempel behövdes inte det, eftersom jag uteslöt frÄn att söka lokalarkivet locale-archive.
För att localedef fungerade med filer i min mapp via variabeln I18NPATH kan du lÀgga till en extra katalog för att söka ingÄngsfiler, och katalogen för att spara binÀra filer kan anges som en vÀg med snedstreck:
$> I18NPATH=~/.local/share/i18n localedef -i ru_RU -f UTF-8 ~/.local/lib/locale/ru_MY.UTF-8POSIX antar att i LANG kan skriva absoluta vÀgar till kataloger med lokalfiler som börjar med snedstreck, men glibc pÄ Linux alla vÀgar anses frÄn basmappen, som kan överskrivas genom variabeln LOCPATH. Efter att ha installerat LOCPATH=~/.local/lib/locale/ kommer alla filer relaterade till lokalisering att sökas endast i min mapp. Arkivet med lokaliteter kommer att ignoreras nÀr den instÀllda variabeln LOCPATH Àr instÀlld.
HÀr Àr det avgörande testet:
$> LANG=ru_MY.UTF-8 LOCPATH=~/.local/lib/locale/ sort buhg.txt
Abakanov Michail;mÄlare
Jolkina Ella;kranoperatör
Ivanov Andrej;svetsare
Ivanova Alla;advokatUpp med humöret! Vi gjorde det!
Arbeta med felen
Jag har redan besvarat frÄgorna om sortering av strÀngar som stÀlldes i början, men det finns fortfarande nÄgra frÄgor om fel - synliga och osynliga.
LÄt oss ÄtervÀnda till den ursprungliga uppgiften.
Och programmet sort och programmet join anvĂ€nder samma strĂ€ngjĂ€mförelsefunktioner frĂ„n glibc. Hur kom det sig att join gav ett sorteringsfel pĂ„ strĂ€ngar sorterade av kommandot sort i lokaliteten en_US.UTF-8? ĐŃĐČĐ”Ń ĐżŃĐŸŃŃ: sort jĂ€mför hela strĂ€ngen, medan join bara jĂ€mför nyckeln, som som standard Ă€r början av strĂ€ngen fram till den första blankstegssymbolen. I mitt exempel ledde detta till ett felmeddelande eftersom sorteringen av de första orden i strĂ€ngarna inte stĂ€mde överens med sorteringen av hela strĂ€ngar.
Lokalitet "C" garanterar att de sorterade raderna innehÄller de inledande delstrÀngarna fram till det första mellanslaget sorterade, men det döljer bara ett fel. Det gÄr att hitta sÄdana data (mÀnniskor med samma efternamn men olika förnamn) som utan felmeddelande skulle ge ett felaktigt resultat vid sammanslagning av filer. Om vi vill att join sammanslÄ rader frÄn filer baserat pÄ namn, sÄ Àr den rÀtta metoden att uttryckligen ange fÀltseparatorn och sortera efter nyckelfÀltet, istÀllet för hela raden. I detta fall kommer sammanslagningen att ske korrekt och det kommer inte att uppstÄ nÄgra fel i nÄgon lokal:
$> sort -t ; -k 1 buhg.txt > buhg.srt
$> sort -t ; -k 1 mail.txt > mail.srt
$> join -t ; buhg.srt mail.srt > resultEtt exempel som har körts framgÄngsrikt i en kodning CP1251 har ocksÄ ett annat fel. Faktum Àr att i alla kÀnda distributioner Linux finns det ingen kompilerad lokal i paketen ru_RU.CP1251. Om den kompilerade lokalen inte hittas, sÄ sort anvÀnder den tyst byte-för-byte jÀmförelse, vilket vi just observerade.
Förresten finns det en annan liten bugg relaterad till avsaknaden av kompilerade lokaler. Kommando LOCPATH=/tmp locale -a visar en lista över alla lokaler i locale-archive, men med den instÀllda variabeln LOCPATH Àr dessa lokaler otillgÀngliga för alla program (inklusive sjÀlva locale).
$> LOCPATH=/tmp locale -a | grep en_US
locale: Kan inte stÀlla in LC_CTYPE till standardlokal: Ingen sÄdan fil eller katalog
locale: Kan inte stÀlla in LC_MESSAGES till standardlokal: Ingen sÄdan fil eller katalog
locale: Kan inte stÀlla in LC_COLLATE till standardlokal: Ingen sÄdan fil eller katalog
en_US
en_US.iso88591
en_US.iso885915
en_US.utf8
$> LC_COLLATE=en_US.UTF-8 sort --debug
sort: anvÀnder 'en_US.UTF-8' sorteringsregler
$> LOCPATH=/tmp LC_COLLATE=en_US.UTF-8 sort --debug
sort: anvÀnder enkel byte-jÀmförelseSlutsats
Om du Àr en programmerare som Àr van vid att betrakta strÀngar som en uppsÀttning bytes, sÄ Àr ditt val LC_COLLATE=C.
Om du Àr lingvist eller ordboksförfattare, sÄ Àr det bÀst att kompilera din egen lokal.
Om du Àr en vanlig anvÀndare, sÄ behöver du bara vÀnja dig vid att kommandot ls -a returnerar filer som börjar med en punkt, blandat med filer som börjar med en bokstav, och Midnight Commander, som anvÀnder sina interna funktioner för att sortera namn, placerar filer som börjar med en punkt i början av listan.
LĂ€nkar
KĂ€lla: habr.com
