Hoe't Linux's sortearje strings

Ynlieding

It begon allegear mei in koart skript dat adresynformaasje kombinearje soe email meiwurkers krigen fan 'e list mei brûkers fan mailinglist, mei wurknimmersposysjes krigen fan' e HR-ôfdielingsdatabase. Beide listen waarden eksportearre nei Unicode-tekstbestannen UTF-8 en bewarre mei Unix line eintsjes.

Ynhâld mail.txt

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

Ynhâld buhg.txt

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

Om te fusearjen, waarden de bestannen sorteare troch it Unix-kommando sort en yntsjinne by de ynput fan it Unix-programma join, dy't ûnferwacht mislearre mei in flater:

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

It sortearjen fan it sortearresultaat mei jo eagen liet sjen dat de sortearring yn 't algemien goed is, mar yn it gefal fan tafallichheden fan manlike en froulike efternammen komme de froulike foar de manlike:

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

It liket op in sortearjende glitch yn Unicode of as in manifestaasje fan feminisme yn it sortearalgoritme. De earste is fansels oannimliker.

Litte wy it foarearst oan 'e kant sette join en fokus op sort. Litte wy besykje it probleem op te lossen mei help fan wittenskiplike poking. Litte wy earst de lokaasje feroarje fan yn 'e FS op ru_RU. Om te sortearjen soe it genôch wêze om de omjouwingsfariabele yn te stellen LC_COLLATE, mar wy sille gjin tiid fergrieme oan lytse dingen:

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

Neat feroare.

Litte wy besykje de bestannen opnij te kodearjen yn in single-byte kodearring:

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

Der is wer neat feroare.

Jo kinne neat dwaan, jo moatte sykje nei in oplossing op it ynternet. D'r is neat direkt oer Russyske efternammen, mar d'r binne fragen oer oare sortearjende nuverheden. Hjir is bygelyks in probleem: unix sort behannelet '-' (dash) karakters as ûnsichtber. Koartsein, de snaren "ab", "aa", "ac" wurde sortearre as "aa", "ab", "ac".

It antwurd is oeral standert: brûk de programmeur locale "C" en do silst wêze bliid. Litte we it besykje:

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

Der is wat feroare. De Ivanovs leinen yn 'e juste folchoarder, hoewol Yolkina ergens glied. Litte wy weromgean nei it orizjinele probleem:

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

It wurke sûnder flaters, lykas it ynternet tasein. En dit nettsjinsteande Yolkina yn 'e earste rigel.

It probleem liket oplost te wêzen, mar foar it gefal, litte wy in oare Russyske kodearring besykje - Windows CP1251:

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

It sortearresultaat sil, frjemd genôch, oerienkomme mei de lokaasje "C", en it hiele foarbyld rint dus sûnder flaters. In soarte fan mystyk.

Ik hâld net fan mystyk yn programmearring, om't it gewoanlik flaters maskearret. Wy sille serieus sjen moatte hoe't it wurket. sort en wat hat it ynfloed? LC_COLLATE .

Oan 'e ein sil ik besykje de fragen te beantwurdzjen:

  • wêrom waarden froulike efternammen ferkeard sortearre?
  • wêrom LANG=ru_RU.CP1251 blykte lykweardich te wêzen LANG=C
  • Wêrom dogge sort и join ferskillende ideeën oer de folchoarder fan sortearre snaren
  • wêrom binne d'r flaters yn al myn foarbylden?
  • úteinlik hoe te sortearjen snaren nei jo smaak

Sortearje yn Unicode

De earste halte sil wêze technyske rapport No.. 10 rjocht Unicode kollaasjealgoritme online unicode.org. It rapport befettet in protte technyske details, dus lit my in koarte gearfetting jaan fan 'e haadideeën.

kollationearjen - "fergelykje" snaren is de basis fan elk sortearalgoritme. De algoritmen sels kinne ferskille ("bubble", "fusearje", "fluch"), mar se sille allegear in ferliking fan in pear snaren brûke om de folchoarder te bepalen wêryn se ferskine.

It sortearjen fan snaren yn natuerlike taal is in frij kompleks probleem. Sels yn 'e ienfâldichste single-byte-kodearrings sil de folchoarder fan letters yn it alfabet, sels op ien of oare manier oars as it Ingelske Latynske alfabet, net mear oerienkomme mei de folchoarder fan' e numerike wearden wêrmei dizze letters kodearre binne. Dus yn it Dútske alfabet de letter Ö stiet tusken О и P, en yn 'e kodearring CP850 hja komt tusken ÿ и Ü.

Jo kinne besykje te abstraheren fan in spesifike kodearring en beskôgje "ideale" letters dy't yn guon folchoarder binne regele, lykas dien wurdt yn Unicode. Kodearrings UTF8, UTF16 of ien-byte KOI8-R (as in beheinde subset fan Unicode nedich is) sil ferskate numerike foarstellings fan letters jaan, mar ferwize nei deselde eleminten fan 'e basistabel.

It docht bliken dat sels as wy bouwe in symboal tafel út 'e kratsje, wy sille net by steat wêze om tawize in universele symboal folchoarder oan it. Yn ferskate nasjonale alfabetten dy't deselde letters brûke, kin de folchoarder fan dizze letters ferskille. Bygelyks yn it Frânsk Æ sil wurde beskôge as in ligature en sortearre as in tekenrige AE. Yn it Noarsk Æ sil in aparte brief, dat leit na Z. Troch de wei, neist ligaturen lykas Æ Der binne letters skreaun mei ferskate symboalen. Dus yn it Tsjechyske alfabet stiet in letter Ch, dy't tusken stiet H и I.

Neist ferskillen yn alfabetten binne d'r oare nasjonale tradysjes dy't de sortearring beynfloedzje. Benammen de fraach ûntstiet: yn hokker folchoarder moatte wurden besteande út haad- en lytse letters yn it wurdboek komme? Sortearjen kin ek beynfloede wurde troch it brûken fan ynterpunksjes. Yn it Spaansk wurdt in omkeard fraachteken brûkt oan it begjin fan in fraachpetear (Hâldsto fan muzyk?). Yn dit gefal is it fanselssprekkend dat fraachsinnen net bûten it alfabet yn in apart kluster groepearre wurde moatte, mar hoe kinne jo rigels sortearje mei oare ynterpunksjes?

Ik sil net dwaen oer it sortearjen fan snaren yn talen dy't hiel oars binne as Jeropeeske. Tink derom dat yn talen mei in skriuwrjochting fan rjochts nei lofts of fan boppe nei ûnderen, tekens yn rigels nei alle gedachten wurde opslein yn lêsfolchoarder, en sels net-alfabetyske skriuwsystemen hawwe har eigen manieren om rigels karakter foar karakter te ordenen . Bygelyks, hiëroglyfen kinne wurde oardere op styl (Sineeske karakters toetsen) of troch útspraak. Om earlik te wêzen, ik haw gjin idee hoe't emoji's moatte wurde regele, mar jo kinne ek wat foar har betinke.

Op grûn fan de hjirboppe neamde funksjes waarden de basiseasken foar it fergelykjen fan snaren basearre op Unicode-tabellen formulearre:

  • fergeliking fan snaren is net ôfhinklik fan 'e posysje fan karakters yn' e koadetabel;
  • sekwinsjes fan karakters dy't ien karakter foarmje, wurde redusearre ta kanonike foarm (A + de boppeste sirkel is itselde as Å);
  • By it fergelykjen fan snaren, wurdt in karakter beskôge yn 'e kontekst fan' e tekenrige en, as it nedich is, kombinearre mei syn buorlju yn ien ienheid fan ferliking (Ch yn it Tsjechysk) of is ferdield yn ferskate (Æ yn it Frânsk);
  • alle nasjonale skaaimerken (alfabet, haadletters / lytse letters, ynterpunksje, folchoarder fan skriuwtypen) moatte wurde konfigurearre oant de hânlieding tawizing fan 'e oarder (emoji);
  • fergeliking is net allinich wichtich foar sortearjen, mar ek op in protte oare plakken, bygelyks foar it opjaan fan rigelgebieten (ferfanging fan {A... z} yn bash);
  • de fergeliking moat frij gau dien wurde.

Derneist formulearre de auteurs fan it rapport fergelikingseigenskippen wêrop algoritme-ûntwikkelders net moatte fertrouwe op:

  • it fergelikingsalgoritme moat gjin aparte set karakters foar elke taal nedich wêze (Russyske en Oekraynske talen diele de measte Syrillyske tekens);
  • de fergeliking moat net betrouwe op de folchoarder fan tekens yn Unicode tabellen;
  • stringgewicht moat gjin attribút fan 'e snaar wêze, om't deselde snaar yn ferskate kulturele konteksten ferskillende gewichten hawwe kin;
  • rigegewichten kinne feroarje by gearfoegjen of splitsen (fan x < y dat folget net xz < yz);
  • ferskillende snaren dy't hawwe deselde gewichten wurde beskôge as lykweardich út it eachpunt fan it sortearjen algoritme. Yntrodusearje ekstra oardering fan sokke snaren is mooglik, mar it kin degradearje prestaasjes;
  • By werhelle sortearring kinne rigen mei deselde gewichten wiksele wurde. Robustheid is in eigenskip fan in spesifike sortearring algoritme, en net in eigenskip fan in string ferliking algoritme (sjoch de foarige paragraaf);
  • Sortearregels kinne yn 'e rin fan' e tiid feroarje as kulturele tradysjes ferfine / feroarje.

Ek wurdt bepaald dat it fergelikingsalgoritme neat wit oer de semantyk fan de stringen dy't ferwurke wurde. Stringen dy't allinich út sifers besteane moatte dus net as nûmers fergelike wurde, en yn listen mei Ingelske nammen it lidwurd (Beatles, op).

Om oan alle oantsjutte easken te foldwaan, wurdt in algoritme foar tafelsortearring op meardere nivo's (eigentlik fjouwer nivo's) foarsteld.

Eartiids wurde de tekens yn 'e tekenrige fermindere ta kanonike foarm en groepearre yn ienheden fan ferliking. Elke ienheid fan ferliking wurdt tawiisd ferskate gewichten oerienkommende mei ferskate nivo's fan ferliking. De gewichten fan fergeliking-ienheden binne eleminten fan oardere sets (yn dit gefal, heule getallen) dy't mear of minder kinne wurde fergelike. Spesjale betsjutting NEGEN (0x0) betsjut dat op it oerienkommende ferlikingsnivo dizze ienheid net belutsen is by de fergeliking. De fergeliking fan snaren kin ferskate kearen wurde werhelle, mei de gewichten fan 'e oerienkommende nivo's. Op elk nivo wurde de gewichten fan 'e fergelikingsienheden fan twa rigen sequentieel fergelike mei elkoar.

Yn ferskate ymplemintaasjes fan it algoritme foar ferskate nasjonale tradysjes kinne de wearden fan 'e koeffizienten ferskille, mar de Unicode-standert omfettet in basistabel fan gewichten - "Standert Unicode Collation Element Table" (DUCET). Ik soe graach opmerke dat it ynstellen fan de fariabele LC_COLLATE is eins in yndikaasje fan de seleksje fan it gewicht tafel yn de string ferliking funksje.

Weighting koeffizienten DUCET as folget regele:

  • op it earste nivo wurde alle letters fermindere ta deselde haadletters, diakrityske tekens wurde wegere, ynterpunksjes (net allegear) wurde negearre;
  • op it twadde nivo wurdt allinnich mei diakrityk rekken holden;
  • op it tredde nivo, allinnich gefal wurdt rekken holden;
  • op it fjirde nivo wurde allinnich ynterpunksjes yn rekken brocht.

De ferliking fynt plak yn ferskate passes: earst wurde de koeffizienten fan it earste nivo ferlike; as de gewichten gearfalle, dan wurdt in werhelle ferliking mei de gewichten fan it twadde nivo útfierd; dan faaks in tredde en in fjirde.

De fergeliking einiget as de rigen oerienkommende ienheden fan ferliking mei ferskillende gewichten befetsje. Rigen dy't gelikense gewichten hawwe op alle fjouwer nivo's wurde as lyk oan elkoar beskôge.

Dit algoritme (mei in bosk oanfoljende technyske details) joech de namme oan rapport nûmer 10 - "Unicode Collation Algorithm" (ACU).

Dit is wêr't it sorteargedrach út ús foarbyld wat dúdliker wurdt. It soe moai wêze om it te fergelykjen mei de Unicode-standert.

Om ymplemintaasjes te testen ACU der is in spesjaal de test, gebrûk gewichten triem, ymplemintaasje DUCET. Jo kinne allerhanne grappige dingen fine yn it skaalbestân. Bygelyks, d'r is de folchoarder fan mahjong en Europeeske domino's, lykas de folchoarder fan kleuren yn in dek kaarten (symboal 1F000 en fierder). De kaartpakken wurde pleatst neffens de regels fan brêge - PCBT, en de kaarten yn 'e kleur binne yn' e folchoarder T, 2,3, XNUMX ... K.

Hânmjittich kontrolearjen dat rigen binne sortearre korrekt neffens DUCET soe frij ferfeelsum wêze, mar, gelokkich foar ús, is d'r in foarbyldige ymplemintaasje fan 'e bibleteek foar wurkjen mei Unicode - "Ynternasjonale komponinten foar Unicode"(ICU).

Op de webside fan dizze bibleteek, ûntwikkele yn IBM, der binne demo siden, ynklusyf string ferliking algoritme side. Wy ynfiere ús testrigels mei standertynstellingen en, sjoch, wy krije perfekte Russyske sortearring.

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

Troch de wei, op 'e webside ICU Jo kinne in ferdúdliking fine fan it fergelikingsalgoritme by it ferwurkjen fan ynterpunksjes. Yn 'e foarbylden Kollaasje FAQ apostrof en koppelteken wurde negearre.

Unicode holp ús, mar sykje nei redenen foar frjemd gedrach sort в linux sil earne oars hinne moatte.

Sortearje yn glibc

Fluch werjefte fan de boarne koades fan it nut sort fan GNU Core Utils liet sjen dat yn it nut sels, lokalisaasje komt del op it printsjen fan de hjoeddeiske wearde fan de fariabele LC_COLLATE as jo rinne yn debugmodus:

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

String-fergelikingen wurde útfierd mei de standertfunksje strcoll, wat betsjut dat alles nijsgjirrich is yn 'e bibleteek glibc.

op wiki it projekt glibc wijd oan string ferliking ien alinea. Ut dizze paragraaf kin begrepen wurde dat yn glibc sortearring is basearre op in algoritme al bekend oan ús ACU (It Unicode-kollaasjealgoritme) en/of op in standert tichtby it ISO 14651 (Ynternasjonale string oardering en ferliking). Oangeande de lêste standert, it moat opmurken wurde dat op 'e side standards.iso.org ISO 14651 offisjeel iepenbier beskikber ferklearre, mar de oerienkommende keppeling liedt ta in net-besteand side. Google jout ferskate siden werom mei keppelings nei offisjele siden dy't oanbiede om in elektroanyske kopy fan 'e standert te keapjen foar hûndert euro, mar op' e tredde of fjirde side fan sykresultaten binne d'r ek direkte keppelings nei PDF. Yn it algemien is de standert praktysk net oars as ACU, mar is saai om te lêzen omdat it gjin dúdlike foarbylden fan nasjonale skaaimerken fan string sortearring befettet.

De meast nijsgjirrige ynformaasje oer wiki der wie in keppeling nei bug tracker mei in diskusje oer de ymplemintaasje fan string ferliking yn glibc. Ut de diskusje is dat te learen glibc brûkt om snaren te fergelykjen ISOpersoanlike tafel De Common Template Table (CTT), wêrfan it adres te finen is yn 'e applikaasje A standert ISO 14651. Tusken 2000 en 2015 dizze tabel yn glibc hie gjin ûnderhâlder en wie hiel oars (op syn minst ekstern) fan de hjoeddeiske ferzje fan de standert. Fan 2015 oant 2018 hat oanpassing oan 'e nije ferzje fan' e tabel plakfûn, en no hawwe jo in kâns om yn it echte libben in nije ferzje fan 'e tabel te moetsjen (CentOS 8), en âld (CentOS 7).

No't wy alle ynformaasje hawwe oer it algoritme en helptafels, kinne wy ​​​​weromgean nei it orizjinele probleem en begripe hoe't jo stringen yn 'e Russyske lokaasje korrekt sortearje kinne.

ISO 14651/14652

Boarne koade fan de tabel wy binne ynteressearre yn CTT op de measte distribúsjes linux stiet yn de katalogus /usr/share/i18n/locales/. De tabel sels is yn 'e triem iso14651_t1_common. Dan is dit de triemrjochtline kopiearje iso14651_t1_common opnommen yn it bestân iso14651_t1, dy't, op syn beurt, is opnaam yn nasjonale triemmen, ynklusyf yn 'e FS и ru_RU. Op de measte distribúsjes linux alle boarne triemmen binne opnaam yn de basis ynstallaasje, mar as se binne net oanwêzich, jo moatte ynstallearje in ekstra pakket út de distribúsje.

Triemstruktuer iso14651_t1 kin ferskriklik verbose lykje, mei net foar de hân lizzende regels foar it bouwen fan nammen, mar as jo it sjogge, is alles frij simpel. De struktuer wurdt beskreaun yn 'e standert ISO 14652, wêrfan in kopy kin wurde downloade fan 'e webside iepen-std.org. In oare beskriuwing fan it bestânsformaat kin yn lêzen wurde spesifikaasjes POSIX от OpenGroup. As alternatyf foar it lêzen fan de standert kinne jo de boarnekoade fan 'e funksje studearje collate_read в glibc/locale/programs/ld-collate.c.

De triemstruktuer sjocht der sa út:

Standert wurdt it karakter brûkt as ûntsnappingsteken, en it ein fan 'e rigel nei it # karakter is in opmerking. Beide symboalen kinne wurde opnij definieare, wat is dien yn 'e nije ferzje fan' e tabel:

escape_char /
comment_char %

It bestân sil tokens befetsje yn it formaat of (Wêr x - heksadesimale sifer). Dit is de heksadesimale foarstelling fan Unicode-koadepunten yn 'e kodearring UCS-4 (UTF-32). Alle oare eleminten yn hoeke heakjes (ynklusyf , <2> en sa) wurde beskôge as ienfâldige tekenrige konstanten dy't net folle betsjutting hawwe bûten kontekst.

String LC_COLLATE fertelt ús dat it folgjende begjint de gegevens dy't de fergeliking fan snaren beskriuwe.

Earst wurde nammen oanjûn foar de gewichten yn 'e ferlikingstabel en nammen foar de symboalkombinaasjes. Yn 't algemien hearre de twa soarten nammen ta twa ferskillende entiteiten, mar yn' e eigentlike triem binne se mingd. De nammen fan de gewichten wurde oantsjutte troch it kaaiwurd collating-symboal (fergelikingskarakter) om't by fergelykjen Unicode-karakters dy't deselde gewichten hawwe sille wurde beskôge as lykweardige karakters.

De totale lingte fan 'e seksje yn' e hjoeddeistige triemferzje is sawat 900 rigels. Ik helle foarbylden út ferskate plakken om de willekeurigens fan nammen en ferskate soarten syntaksis sjen te litten.

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

  • collating-symboal logs in string OSMANYA yn 'e tabel fan nammen fan skalen
  • collating-symboal .. registrearret in folchoarder fan nammen besteande út in foarheaksel S en heksadesimale numerike efterheaksel fan 1D000 до 1D35F.
  • FFFF в collating-symboal liket op in grut net ûndertekene hiel getal yn heksadesimale, mar it is gewoan in namme dy't der op lykje kin
  • namme betsjut koade punt yn kodearring UCS-4
  • sammelje-elemint fan" " registrearret in nije namme foar in pear Unicode-punten.

Sadree't de nammen fan de gewichten binne definiearre, de eigentlike gewichten wurde oantsjutte. Om't allinich grutter-as-minder relaasjes fan belang binne yn ferliking, wurde de gewichten bepaald troch in ienfâldige folchoarder fan listnammen. De "lichtere" gewichten wurde earst neamd, dan de "swierdere". Lit my jo herinnerje dat elk Unicode-karakter fjouwer ferskillende gewichten wurdt tawiisd. Hjir binne se kombinearre yn ien oardere folchoarder. Yn teory kin elke symboalyske namme brûkt wurde op ien fan 'e fjouwer nivo's, mar opmerkings jouwe oan dat de ûntwikkelders nammen mentaal skiede yn nivo's.

% 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

Ta beslút, de eigentlike gewicht tafel.

De seksje gewichten is ynsletten yn keyword rigels order_start и order_end. Ekstra opsjes order_start bepale yn hokker rjochting rigen wurde skansearre op elk nivo fan ferliking. De standertynstelling is foarút. It lichem fan 'e seksje bestiet út rigels dy't befetsje it symboal koade en syn fjouwer gewichten. De karakterkoade kin wurde fertsjintwurdige troch it karakter sels, in koadepunt, of in symboalyske namme dy't earder definiearre is. Gewichten kinne ek jûn wurde oan symboalyske nammen, koadepunten of de symboalen sels. As koadepunten of karakters wurde brûkt, is har gewicht itselde as de numerike wearde fan it koadepunt (posysje yn 'e Unicode-tabel). Karakters dy't net eksplisyt oantsjutte (lykas ik begryp) wurde beskôge as tawiisd oan 'e tabel mei in primêr gewicht dat oerienkomt mei de posysje yn' e Unicode-tabel. Spesjale gewicht wearde NEGEARJE betsjut dat it symboal wurdt negearre op it passend nivo fan ferliking.

Om de struktuer fan 'e skalen te demonstrearjen, haw ik trije frij dúdlike fragminten keazen:

  • karakters dy't folslein negearre
  • symboalen lykweardich oan it getal trije yn de earste twa nivo
  • it begjin fan it Syrillyske alfabet, dat gjin diakrityske tekens befettet, en dus benammen op it earste en tredde nivo sortearre wurdt.

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

No kinne jo weromgean nei it sortearjen fan de foarbylden fan it begjin fan it artikel. De hinderlaag leit yn dit diel fan 'e gewichtstabel:

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

It kin sjoen wurde dat yn dizze tabel de ynterpunksjes út 'e tabel ASCII (ynklusyf romte) wurdt hast altyd negearre by it fergelykjen fan snaren. De ienige útsûnderingen binne rigels dy't oerienkomme yn alles útsein ynterpunksje tekens fûn yn oerienkommende posysjes. De rigels fan myn foarbyld (nei sortearjen) foar it fergelikingsalgoritme sjogge der sa út:

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

Yn betinken nommen dat yn 'e tabel fan skalen, haadletters yn Russysk komme nei lytse letters (op it tredde nivo swierder as ), sjocht de sortearring absolút korrekt.

By it ynstellen fan in fariabele LC_COLLATE=C in spesjale tabel wurdt laden dy't spesifisearret in byte-by-byte ferliking

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

Om't yn Unicode it koadepunt Ё foar A komt, wurde de stringen dêroer sortearre.

Tekst en binêre tabellen

Fansels is stringfergeliking in ekstreem gewoane operaasje, en tabelparsing CTT nochal in kostbere proseduere. Om de tagong ta de tabel te optimalisearjen, wurdt it kompilearre yn binêre foarm mei it kommando localdef.

team localdef akseptearret as parameter in bestân mei in tabel mei nasjonale skaaimerken (opsje -i), wêryn alle karakters wurde fertsjintwurdige troch Unicode-punten, en in bestân mei korrespondinsje tusken Unicode-punten en karakters fan in spesifike kodearring (opsje -f). As gefolch fan it wurk wurde binêre bestannen makke foar de locale mei de namme oantsjutte yn 'e lêste parameter.

glibc stipet twa binêre bestânsformaten: "tradisjoneel" en "modern".

It tradisjonele formaat betsjut dat de namme fan 'e locale de namme is fan' e submap yn /usr/lib/locale/. Dizze submap bewarret binêre bestannen LC_COLLATE, LC_CTYPE, LC_TIME ensafuorthinne. Map LC_IDENTIFICATION befettet de formele namme fan 'e locale (dy't oars kin wêze as de mapnamme) en opmerkings.

It moderne formaat omfettet it opslaan fan alle lokaasjes yn ien argyf /usr/lib/locale/locale-archive, dat wurdt yn kaart brocht oan it firtuele ûnthâld fan alle prosessen dy't brûke glibc. De lokaasjenamme yn it moderne formaat is ûnderwurpen oan wat kanonisaasje - allinich sifers en letters fermindere ta lytse letters bliuwe yn 'e kodearringnammen. Sa ru_RU.KOI8-R, sil wurde bewarre as ru_RU.koi8r.

Ynfierbestannen wurde socht yn 'e hjoeddeistige map, lykas yn mappen /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ foar triemmen CTT en kodearring triemmen, respektivelik.

Bygelyks, it kommando

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

sil it bestân kompilearje /usr/share/i18n/locales/ru_RU mei help fan kodearring triem /usr/share/i18n/charmaps/MAC-CYRILLIC.gz en bewarje it resultaat yn /usr/lib/locale/locale-archive ûnder de namme ru_RU.maccyrillic

As jo ​​ynstelle de fariabele LANG = en_US.UTF-8 то glibc sil sykje nei locale binaries yn 'e folgjende folchoarder fan bestannen en mappen:

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

As in lokaasje foarkomt yn sawol tradisjonele as moderne formaten, dan wurdt prioriteit jûn oan de moderne.

Jo kinne de list mei kompilearre lokaasjes besjen mei it kommando locale -a.

It tarieden fan jo ferlikingstabel

No, bewapene mei de kennis, kinne jo jo eigen ideale tabel foar snaarfergeliking oanmeitsje. Dizze tabel moat de Russyske letters korrekt fergelykje, ynklusyf de letter Ё, en tagelyk ynterpunksjetekens yn rekken hâlde mei de tabel ASCII.

It proses fan it tarieden fan jo eigen sorteartabel bestiet út twa stadia: it bewurkjen fan de gewichtstabel en it kompilearjen yn binêre foarm mei it kommando localdef.

Om de fergelikingstabel oan te passen mei minimale bewurkingskosten, yn it formaat ISO 14652 Seksjes foar it oanpassen fan de gewichten fan in besteande tafel wurde foarsjoen. De seksje begjint mei in kaaiwurd opnij oarderje-na en it oanjaan fan de posysje wêrnei't de ferfanging wurdt útfierd. De seksje einiget mei de line opnij oarderje-ein. As it nedich is om ferskate seksjes fan 'e tabel te korrigearjen, dan wurdt in seksje makke foar elke sa'n seksje.

Ik kopiearre nije ferzjes fan de triemmen iso14651_t1_common и ru_RU út de repository glibc nei myn thúsmap ~/.local/share/i18n/locales/ en de seksje wat bewurke LC_COLLATE в ru_RU. Nije ferzjes fan bestannen binne folslein kompatibel mei myn ferzje glibc. As jo ​​​​âldere ferzjes fan bestannen wolle brûke, moatte jo de symboalyske nammen feroarje en it plak wêr't de ferfanging begjint yn 'e tabel.

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

Yn feite soe it nedich wêze om de fjilden yn te feroarjen LC_IDENTIFICATION sadat se wize op de lokaasje ru_MY, mar yn myn foarbyld wie dit net fereaske, om't ik it argyf útsletten fan it sykjen nei lokaasjes locale-argyf.

dat localdef wurke mei triemmen yn myn map fia in fariabele I18NPATH Jo kinne in ekstra map taheakje om te sykjen nei ynfierbestannen, en de map om binêre triemmen op te slaan kin opjûn wurde as in paad mei slashes:

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

POSIX giet der fan út dat yn KREKT kinne jo skriuwe absolute paden nei mappen mei locale triemmen, begjinnend mei in foarút slash, mar glibc в linux alle paden wurde teld út de basis triemtafel, dat kin wurde oerskreaun troch in fariabele LOCPATH. Nei ynstallaasje LOCPATH=~/.local/lib/locale/ alle bestannen relatearre oan lokalisaasje sille allinich yn myn map wurde socht. Argyf fan lokaasjes mei de fariabele set LOCPATH negearre.

Hjir is de beslissende test:

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

Hoera! Wy hawwe it dien!

Wurkje op bugs

Ik haw de oan it begjin stelde fragen oer snaarsortering al beäntwurde, mar d'r binne noch in pear fragen oer flaters - sichtber en ûnsichtber.

Litte wy weromgean nei it orizjinele probleem.

En it programma sort en programma join brûke deselde string ferliking funksjes út glibc. Hoe kaam it dat join joech in sortearring flater op rigen sortearre troch it kommando sort yn locale en_US.UTF-8? It antwurd is ienfâldich: sort fergeliket de hiele string, en join fergeliket allinich de kaai, dy't standert it begjin fan 'e tekenrige is oant it earste wite romte-karakter. Yn myn foarbyld resultearre dit yn in flatermelding om't de sortearring fan de earste wurden yn de rigels net oerienkomt mei de sortearring fan de folsleine rigels.

Locale "C" garandearret dat yn sortearre snaren de earste substrings oant de earste romte ek wurde sorteare, mar dit maskert allinich de flater. It is mooglik om gegevens te selektearjen (minsken mei deselde efternammen, mar ferskillende foarnammen) dy't sûnder it flaterberjocht in ferkeard resultaat fan it gearfoegjen fan bestân jouwe. As wy wolle join gearfoege triemrigels mei folsleine namme, dan soe de juste manier wêze om de fjildskieding eksplisyt op te jaan en te sortearjen op it kaaifjild, en net troch de heule rigel. Yn dit gefal sil de fúzje goed trochgean en sille d'r gjin flaters wêze yn elke lokaasje:

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

Súksesfol útfierd foarbyld yn kodearring CP1251 befettet in oare flater. It feit is dat yn alle distribúsjes bekend foar my linux pakketten ûntbrekke kompilearre locale ru_RU.CP1251. As de kompilearre locale net fûn is, dan sort brûkt stil in byte-by-byte ferliking, dat is wat wy observearre.

Trouwens, d'r is in oare lytse glitch yn ferbân mei de ûnberikberens fan kompilearre lokaasjes. Ploech LOCPATH=/tmp locale -a sil in list jaan fan alle lokaasjes yn locale-argyf, mar mei de fariabele set LOCPATH foar alle programma's (ynklusyf de measte lokale) dizze lokaasjes sille net beskikber wêze.

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

konklúzje

As jo ​​​​in programmeur binne dy't wend is om te tinken dat snaren in set fan bytes binne, dan is jo kar LC_COLLATE=C.

As jo ​​in taalkundige of wurdboek gearstaller binne, dan kinne jo better kompilearje yn jo lokaasje.

As jo ​​​​in ienfâldige brûker binne, dan moatte jo gewoan wenne oan it feit dat it kommando ls -a útfiert triemmen begjinnend mei in stip mingd mei triemmen begjinnend mei in letter, en Middernacht kommandant, dy't syn ynterne funksjes brûkt om nammen te sortearjen, set triemmen begjinnend mei in punt oan it begjin fan 'e list.

referinsjes

Ferslach No.. 10 Unicode collation algoritme

Karaktergewichten op unicode.org

ICU - ymplemintaasje fan in bibleteek foar wurkjen mei Unicode fan IBM.

Sortearjen test mei help ICU

Karakter gewicht yn ISO 14651

Beskriuwing fan it bestânsformaat mei skalen ISO 14652

Diskusje oer string ferliking yn glibc

Boarne: www.habr.com

Add a comment