Cume a sorta di Linux ordina e stringhe

Introduzione

Tuttu hà cuminciatu cù un brevi script chì duvia cumminà l'infurmazioni di l'indirizzu e-mail impiegati ottenuti da a lista di l'utilizatori di mailing list, cù i posti di l'impiegati ottenuti da a basa di dati di u dipartimentu HR. E duie liste sò state esportate in i fugliali di testu Unicode UTF-8 è salvatu cù a fine di linea Unix.

Cuntenutu mail.txt

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

Cuntenutu buhg.txt

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

Per unisce, i schedari sò stati urdinati da u cumandamentu Unix magnusca è sottumessi à l'input di u prugramma Unix Unete, chì hà fiascatu inaspettatamente cù un errore:

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

Videndu u risultatu di a classificazione cù i vostri ochji hà dimustratu chì, in generale, a classificazione hè curretta, ma in u casu di coincidenze di cognomi maschili è femini, i femini sò prima di i masci:

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

Sembra un glitch di classificazione in Unicode o cum'è una manifestazione di feminismu in l'algoritmu di classificazione. U primu hè, sicuru, più plausibile.

Rimettimu per avà Unete è fucalizza nantu magnusca. Pruvemu di risolve u prublema usendu puzziche scientifiche. Prima, cambiamu u locale da in i Stati Uniti nantu ru_RU. Per sorte, saria abbastanza per stabilisce a variabile d'ambiente LC_COLLATE, ma ùn perdemu micca u tempu in trifles:

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

Nunda hà cambiatu.

Pruvemu di ricudificà i fugliali in una codificazione di un byte:

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

Di novu nunda hà cambiatu.

Ùn ci hè nunda chì pudete fà, vi tuccherà à circà una suluzione nant'à Internet. Ùn ci hè nunda direttamente nantu à i cognomi russi, ma ci sò dumande nantu à altre stranezze di classificazione. Per esempiu, quì hè un prublema: Unix sort tratta i caratteri "-" (dash) cum'è invisibili. In corta, i cordi "a-b", "aa", "ac" sò ordinati cum'è "aa", "a-b", "ac".

A risposta hè standard in ogni locu: utilizate u locale di u programatore "C" è sarete felice. Pruvemu:

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

Qualcosa hè cambiatu. L'Ivanov si sò allineati in l'ordine currettu, ancu s'è Yolkina s'hè sbulicatu in qualchì locu. Riturnemu à u prublema originale:

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

Hà travagliatu senza errore, cum'è l'Internet hà prumessu. E questu malgradu Yolkina in a prima linea.

U prublema pare esse risolta, ma solu in casu, pruvemu una altra codificazione russa - Windows CP1251:

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

U risultatu di a classificazione, curiosamente, coinciderà cù u locale "C", è tuttu l'esempiu, dunque, corre senza errori. Una certa mistica.

Ùn mi piace micca a mistica in a prugrammazione perchè di solitu maschera i sbagli. Avemu da guardà seriamente cumu funziona. magnusca è chì affetta ? LC_COLLATE .

À a fine, pruvà à risponde à e dumande:

  • perchè i cognomi femminili sò stati ordinati incorrectamente?
  • perchè LANG=ru_RU.CP1251 risultava equivalenti LANG=C
  • perchè fà magnusca и Unete idee diverse nantu à l'ordine di stringhe ordinate
  • perchè ci sò errori in tutti i mo esempi?
  • infine cumu per sorte e stringhe à u vostru piace

Ordine in Unicode

A prima tappa serà u rapportu tecnicu n ° 10 intitulatu Algoritmu di colazione Unicode Online unicode.org. U rapportu cuntene assai ditaglii tecnichi, dunque lasciami dà un brevi riassuntu di l'idee principali.

collation - stringhe "paragunate" hè a basa di qualsiasi algoritmu di sorte. L'algoritmi stessi ponu differisce ("bubble", "merge", "fast"), ma tutti anu da utilizà un paragone di un paru di corde per determinà l'ordine in quale appariscenu.

Sorting strings in lingua naturale hè un prublema abbastanza cumplessu. Ancu in i codificazioni più simplici di un byte, l'ordine di e lettere in l'alfabetu, ancu in qualchì manera sfarente di l'alfabetu latinu inglese, ùn coinciderà più cù l'ordine di i valori numerichi cù quale sti lettere sò codificate. Allora in l'alfabbetu tedescu a lettera Ö si trova trà О и P, è in a codificazione CP850 ella si mette trà ÿ и Ü.

Pudete pruvà à astrazione da una codificazione specifica è cunsiderà lettere "ideali" chì sò disposti in un certu ordine, cum'è hè fattu in Unicode. Codificazioni UTF8, UTF16 o un byte KOI8-R (se un subset limitatu di Unicode hè necessariu) darà diverse rappresentazioni numeriche di lettere, ma riferite à i stessi elementi di a tabella di basa.

Ci hè chì ancu s'ellu custruemu una tabella di simboli da zero, ùn pudemu micca assignà un ordine di simbulu universale. In diversi alfabeti naziunali chì utilizanu e stesse lettere, l'ordine di sti lettere pò esse diffirenti. Per esempiu, in francese Æ serà cunsideratu una ligatura è ordenatu cum'è una stringa AE. In norvegese Æ serà una lettera separata, chì si trova dopu Z. Per via, in più di ligature cum'è Æ Ci sò lettere scritte cù parechji simboli. Allora in l'alfabetu cecu ci hè una lettera Ch, chì si trova trà H и I.

In più di e differenze in l'alfabetu, ci sò altre tradizioni naziunali chì influenzanu a classificazione. In particulare, a quistione hè: in chì ordine deve esse in u dizziunariu e parolle custituiti da lettere maiuscule è minuscule? A classificazione pò ancu esse affettata da l'usu di i segni di puntuazione. In spagnolu, un puntu d'interrogazione invertitu hè utilizatu à u principiu di una frase interrogativa (Ti piace a musica ?). In questu casu, hè ovvi chì e frasi interrogative ùn deve esse raggruppati in un cluster separatu fora di l'alfabetu, ma cumu per sorte e linee cù altri segni di puntuazione?

Ùn mi stenderaghju micca nantu à a classificazione di stringhe in lingue assai diverse da quelle europee. Nota chì in lingue cù una direzzione di scrittura da diritta à manca o da cima à fondu, i caratteri in e linee sò più prubabilmente almacenati in l'ordine di lettura, è ancu i sistemi di scrittura non alfabetici anu i so modi di urdinà e linee carattere per caratteru. . Per esempiu, i jeroglifi ponu esse urdinati per stile (Tasti di caratteri chinesi) o da a pronuncia. Per esse onesto, ùn aghju micca idea di cumu si deve esse disposti l'emojis, ma pudete ancu inventà qualcosa per elli.

Basatu nantu à e caratteristiche elencate sopra, i requisiti basi per paragunà stringhe basate in tabelle Unicode sò stati formulati:

  • a comparazione di stringhe ùn dipende micca da a pusizione di caratteri in a tabella di codice;
  • e sequenze di caratteri chì formanu un caratteru unicu sò ridotti à a forma canonica (A + u circhiu superiore hè u stessu Å);
  • Quandu si comparanu stringhe, un caratteru hè cunsideratu in u cuntestu di a stringa è, se ne necessariu, cumminatu cù i so vicini in una unità di paragone (Ch in ceco) o hè divisu in parechji (Æ in francese);
  • tutte e caratteristiche naziunali (alfabetu, maiuscule / minuscule, puntuazione, ordine di i tipi di scrittura) deve esse cunfigurate finu à l'assignazione manuale di l'ordine (emoji);
  • a paraguna hè impurtante micca solu per l'ordine, ma ancu in parechji altri lochi, per esempiu per specificà i intervalli di fila (sustituendu {A ... z} in bash);
  • u paragone deve esse fattu abbastanza rapidamente.

Inoltre, l'autori di u rapportu anu formulatu proprietà di paraguni chì i sviluppatori di l'algoritmi ùn deve micca cunfidendu:

  • l'algoritmu di paraguni ùn deve micca bisognu di un settore separatu di caratteri per ogni lingua (lingue russa è ucraina sparte a maiò parte di i caratteri cirillichi);
  • a paraguna ùn deve micca s'appoghjanu nantu à l'ordine di i caratteri in tavule Unicode;
  • u pesu di stringa ùn deve esse un attributu di a stringa, postu chì a stessa stringa in diversi cuntesti culturali pò avè pesi diffirenti;
  • i pesi di fila ponu cambià quandu si fusione o splitting (da x < y ùn seguita micca questu xz < yz);
  • strings differente chì anu u stessu pesu sò cunsiderate uguali da u puntu di vista di l'algoritmu di sorte. L'introduzione di l'ordine supplementu di tali corde hè pussibile, ma pò degrade u rendiment;
  • Durante l'ordinamentu ripetutu, e fila cù i stessi pesi ponu esse scambiati. A robustezza hè una pruprietà di un algoritmu di ordinamentu specificu, è micca una pruprietà di un algoritmu di paragone di stringa (vede u paràgrafu precedente);
  • E regule di classificazione ponu cambià cù u tempu cum'è e tradizioni culturali si raffinanu / cambianu.

Hè ancu stipulatu chì l'algoritmu di paraguni ùn sapi nunda di a semantica di e corde chì sò processate. Cusì, e stringhe chì sò custituiti solu di cifre ùn deve esse paragunate cum'è numeri, è in listi di nomi inglesi l'articulu (Beatles, The).

Per soddisfà tutti questi bisogni, hè prupostu un algoritmu di classificazione di tavulinu multi-livellu (in realtà à quattru livelli).

Prima, i caratteri in a stringa sò ridotti à a forma canonica è raggruppati in unità di paraguni. Ogni unità di paragone hè attribuita parechji pesi chì currispondenu à parechji livelli di paraguni. I pesi di unità di paraguni sò elementi di setti urdinati (in questu casu, interi) chì ponu esse paragunati per più o menu. Significatu particulari IGNURATU (0x0) significa chì à u livellu di paraguni currispundenti sta unità ùn hè micca implicata in a paraguna. A paraguni di corde pò esse ripetuta parechje volte, utilizendu i pesi di i livelli currispundenti. À ogni livellu, i pesi di l'unità di paraguni di duie fila sò sequenzialmente paragunati cù l'altri.

In diverse implementazioni di l'algoritmu per e diverse tradizioni naziunali, i valori di i coefficienti ponu differisce, ma u standard Unicode include una tabella basica di pesi - "Tabella di elementi di collazione Unicode predefinita" (DUCET). Vogliu nutà chì stabilisce a variabile LC_COLLATE hè in realtà un'indicazione di a selezzione di a tabella di pesu in a funzione di paragone di stringa.

Coefficienti di ponderazione DUCET disposti cusì:

  • à u primu livellu, tutte e lettere sò ridutte à u listessu casu, i diacritici sò scartati, i segni di puntuazione (micca tutti) sò ignorati;
  • à u sicondu livellu, solu i diacritici sò cunsiderati;
  • à u terzu livellu, solu casu hè pigliatu in contu;
  • à u quartu livellu, solu i segni di puntuazione sò cunsiderati.

A paraguna si faci in parechji passaghji: prima, i coefficienti di u primu livellu sò paragunati; se i pesi coincide, allora un paragone ripetutu cù i pesi di u sicondu livellu hè realizatu; poi forse un terzu è un quartu.

U paraguni finisci quandu e fila cuntenenu unità di paraguni cù pesi diffirenti. E fila chì anu pesi uguali à tutti i quattru livelli sò cunsiderate uguali à l'altri.

Stu algoritmu (cù una mansa di dettagli tecnichi supplementari) hà datu u nome per rapportà N ° 10 - "Algoritmu di colazioni Unicode" (ACU).

Hè quì chì u cumpurtamentu di sorte da u nostru esempiu diventa un pocu più chjaru. Saria bellu paragunà cù u standard Unicode.

Per pruvà implementazioni ACU ci hè un speziale a prova, usu schedariu di pesi, implementazione DUCET. Pudete truvà ogni tipu di cose divertenti in u schedariu di scale. Per esempiu, ci hè l'ordine di mahjong è domino europei, è ancu l'ordine di vestiti in un mazzo di carte (simbulu 1F000 è più in là). I vestiti di carta sò posti secondu e regule di ponte - PCBT, è e carte in u vestitu sò in a sequenza T, 2,3, XNUMX ... K.

Verificate manualmente chì e fila sò ordinate currettamente secondu DUCET seria abbastanza fastidiosa, ma, per furtuna per noi, ci hè una implementazione esemplare di a biblioteca per travaglià cù Unicode - "Componenti Internaziunali per Unicode«(ICU).

In u situ web di sta biblioteca, sviluppata in IBM, Ci sò e pagine demo, cumprese pagina di l'algoritmu di paragone di stringhe. Entremu in e nostre linee di teste cù paràmetri predeterminati è, eccu, avemu un ordinamentu russu perfettu.

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

Per via, u situ web ICU Pudete truvà una chiarificazione di l'algoritmu di paraguni quandu si tratta di i segni di puntuazione. In esempi FAQ di colazioni l'apostrofu è u trattino sò ignorati.

Unicode ci hà aiutatu, ma cercate ragioni per un cumpurtamentu stranu magnusca в Linux duverà andà in un altru locu.

Sorting in glibc

Vista rapida di i codici fonte di utilità magnusca из GNU Core Utils hà dimustratu chì in l'utilità stessu, a localizazione si riduce à stampà u valore attuale di a variabile LC_COLLATE quandu si esegue in modu di debug:

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

I paraguni di stringhe sò realizati cù a funzione standard strcoll, chì significa chì tuttu interessante hè in a biblioteca glibc.

nantu lontana u prughjettu glibc dedicatu à a comparazione di stringhe un paràgrafu. Da stu paràgrafu si pò capisce chì in glibc L'ordine hè basatu annantu à un algoritmu digià cunnisciutu da noi ACU (L'algoritmu di colazione Unicode) è/o à un standard vicinu à questu ISO 14651 (Ordine è paragone di stringhe internaziunali). In quantu à l'ultimu standard, deve esse nutatu chì nantu à u situ standards.iso.org ISO 14651 ufficialmente dichjarata publicamente dispunibile, ma u ligame currispundente porta à una pagina inesistente. Google torna parechje pagine cù ligami à i siti ufficiali chì offrenu cumprà una copia elettronica di u standard per centu euro, ma in a terza o quarta pagina di risultati di ricerca ci sò ancu ligami diretti à PDF. In generale, u standard ùn hè praticamente micca sfarente da ACU, ma hè più noiosa di leghje perchè ùn cuntene esempi chjaru di caratteristiche naziunali di string sorting.

L'infurmazioni più interessanti nantu à lontana ci era un ligame per bug tracker cù una discussione di l'implementazione di paraguni di stringa in glibc. Da a discussione si pò amparà chì glibc usatu per paragunà strings ISOtavula persunale U Template Table Common (Ctt), l'indirizzu di quale si pò truvà in l'applicazione A standard ISO 14651. Trà u 2000 è u 2015 sta tabella in glibc ùn avia micca un mantene è era assai sfarente (almenu esternamente) da a versione attuale di u standard. Da u 2015 à u 2018, l'adattazione à a nova versione di a tavola hè stata fatta, è avà avete a pussibilità di scuntrà in a vita reale una nova versione di a tavola (CentOS 8), è vechju (CentOS 7).

Avà chì avemu tutte l'infurmazioni nantu à l'algoritmu è e tavule ausiliarii, pudemu vultà à u prublema originale è capiscenu cumu per sorte currettamente e corde in u locale russo.

ISO 14651 / 14652

U codice fonte di a tavola chì ci interessa Ctt nantu à a maiò parte di e distribuzioni Linux hè in u catalogu /usr/share/i18n/locales/. A tavula stessu hè in u schedariu iso14651_t1_common. Allora questu hè a direttiva di u schedariu copia iso14651_t1_common inclusu in u schedariu iso14651_t1, chì, à u turnu, hè inclusu in i schedarii naziunali, cumprese in i Stati Uniti и ru_RU. In a maiò parte di e distribuzioni Linux Tutti i fugliali fonte sò inclusi in a stallazione basica, ma s'ellu ùn sò micca prisenti, avete da installà un pacchettu supplementu da a distribuzione.

Struttura di u schedariu iso14651_t1 pò parè terribilmente verbose, cù regule micca evidenti per a custruzzione di nomi, ma se vi fighjate, tuttu hè abbastanza simplice. A struttura hè descritta in u standard ISO 14652, una copia di quale pò esse scaricata da u situ web open-std.org. Una altra descrizzione di u furmatu di u schedariu pò esse leghje in specificazioni POSIX от OpenGroup. Comu alternativa à leghje u standard, pudete studià u codice fonte di a funzione collate_read в glibc/locale/programs/ld-collate.c.

A struttura di u schedariu s'assumiglia cusì:

Per automaticamente, u caratteru hè utilizatu cum'è un caratteru di escape, è a fine di a linea dopu à u caratteru # hè un cumentu. I dui simboli ponu esse ridefiniti, chì hè ciò chì hè fattu in a nova versione di a tavula:

escape_char /
comment_char %

U schedariu cuntene tokens in u furmatu o (Induva x - cifra esadecimale). Questa hè a rapprisintazioni esadecimale di i punti di codice Unicode in a codificazione UCS-4 (UTF-32). Tutti l'altri elementi in parentesi angulari (cumpresu , <2> è simili) sò cunsiderate custanti di stringa simplici chì anu pocu significatu fora di u cuntestu.

Linea LC_COLLATE ci dice chì dopu principia i dati chì descrizanu u paragone di stringhe.

Prima, i nomi sò specificati per i pesi in a tabella di paraguni è i nomi per i cumminzioni di simboli. In generale, i dui tipi di nomi appartenenu à duie entità diverse, ma in u schedariu propiu sò mischiati. I nomi di i pesi sò specificati da a keyword collating-simbulu (carattere di paragone) perchè quandu si compara, i caratteri Unicode chì anu u stessu pesi seranu cunsiderati caratteri equivalenti.

A durata tutale di a rùbbrica in a revisione attuale di u schedariu hè di circa 900 linee. Aghju tiratu esempi da parechji lochi per vede l'arbitrariu di i nomi è parechji tipi di sintassi.

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-simbulu registra una stringa OSMANYA in a tavula di nomi di scale
  • simbulu-colating .. registra una sequenza di nomi custituiti da un prefissu S è u suffissu numericu esadecimale da 1D000 à 1D35F.
  • FFFF в simbulu di collating s'assumiglia à un grande interu senza signu in esadecimale, ma hè solu un nome chì puderia pare
  • nome significa puntu di codice in codificazione UCS-4
  • Elementu di classificazione da "" registra un novu nome per un paru di punti Unicode.

Una volta chì i nomi di i pesi sò definiti, i pesi reali sò specificati. Siccomu solu rilazioni più grande di menu importa in paraguni, i pesi sò determinati da una sequenza simplice di nomi di lista. I pesi "più ligeri" sò listati prima, dopu i più "pesanti". Lasciami ricurdà chì ogni caratteru Unicode hè assignatu quattru pesi diffirenti. Quì sò cumminati in una sola sequenza urdinata. In teoria, ogni nome simbolicu puderia esse usatu à qualsiasi di i quattru livelli, ma i cumenti indicanu chì i sviluppatori si separanu mentalmente i nomi in livelli.

% 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

Infine, u tavulu di pesu attuale.

A sezione di pesi hè chjusa in linee di keyword ordine_iniziu и ordine_fine. Opzioni extra ordine_iniziu determina in quale direzzione e fila sò scansate à ogni livellu di paraguni. U paràmetru predeterminatu hè avanti. U corpu di a rùbbrica hè custituitu di linii chì cuntenenu u codice di simbulu è i so quattru pesi. U codice di u caratteru pò esse rapprisintatu da u caratteru stessu, un puntu di codice, o un nome simbolicu definitu prima. Pesi pò ancu esse datu à nomi simbolichi, punti di codice, o i simboli stessi. Se i punti di codice o caratteri sò usati, u so pesu hè uguali à u valore numericu di u puntu di codice (pusizioni in a tabella Unicode). I caratteri chì ùn anu micca specificatu esplicitamente (cum'è aghju capitu) sò cunsiderati assignati à a tavula cù un pesu primariu chì currisponde à a pusizione in a tavola Unicode. Valore di pesu speciale Ignurà significa chì u simbulu hè ignoratu à u livellu appropritatu di paraguni.

Per dimustrà a struttura di e scale, aghju sceltu trè frammenti abbastanza evidenti:

  • caratteri chì sò completamente ignorati
  • simbuli equivalenti à u numeru trè in i primi dui livelli
  • u principiu di l'alfabetu cirillicu, chì ùn cuntene micca diacritics, è per quessa hè ordinatu principarmenti da u primu è u terzu livellu.

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

Avà pudete turnà à sorte l'esempii da u principiu di l'articulu. L'emboscata si trova in questa parte di a tavola di pesi:

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

Pò esse vistu chì in questa tavula i segni di puntuazione da a tavula ASCII (cumpresu u spaziu) hè casi sempre ignoratu quandu si comparanu strings. L'unicu eccezzioni sò e linee chì currispondenu in tuttu, eccettu i segni di puntuazione truvati in pusizioni currispondenti. I linii da u mo esempiu (dopu à l'ordine) per l'algoritmu di paraguni sò cusì:

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

Cunsiderendu chì in a tavula di scale, lettere majuscule in russo vene dopu lettere minuscule (à u terzu livellu più pesante cà ), a classificazione pare assolutamente curretta.

Quandu stabilisce una variabile LC_COLLATE=C una tavola speciale hè caricata chì specifica una comparazione byte per byte

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

Siccomu in Unicode u puntu di codice Ё vene prima di A, i stringi sò ordinati in cunfurmità.

Testu è tabelle binari

Ovviamente, a comparazione di stringa hè una operazione estremamente cumuna, è l'analisi di a tavola Ctt una prucedura abbastanza costosa. Per ottimisà l'accessu à a tavula, hè compilatu in forma binaria cù u cumandimu localdef.

squadra localdef accetta cum'è parametri un schedariu cù una tabella di caratteristiche naziunali (opzione -i), in quale tutti i caratteri sò rapprisentati da punti Unicode, è un schedariu di currispundenza trà punti Unicode è caratteri di una codificazione specifica (opzione -f). In u risultatu di u travagliu, i schedarii binari sò creati per u locale cù u nome specificatu in l'ultimu paràmetru.

glibc supporta dui furmati di schedariu binari: "tradiziunale" è "modernu".

U formatu tradiziunale significa chì u nome di u locale hè u nome di u subdirectory in /usr/lib/locale/. Stu subdirectory guarda i schedari binari LC_COLLATE, LC_CTYPE, LC_TIME eccetera. File LC_IDENTIFICAZIONE cuntene u nome formale di u locale (chì pò esse diversu da u nome di u cartulare) è i cumenti.

U formatu mudernu implica l'almacenamiento di tutti i locali in un unicu archiviu /usr/lib/locale/locale-archive, chì hè mappatu à a memoria virtuale di tutti i prucessi chì utilizanu glibc. U nome locale in u formatu mudernu hè sottumessu à qualchì canonizazione - solu i numeri è e lettere ridutte à minuscule restanu in i nomi di codificazione. Allora ru_RU.KOI8-R, serà salvatu cum'è ru_RU.koi8r.

I schedarii di input sò cercati in u cartulare attuale, è ancu in i cartulari /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ per i schedari Ctt è i schedarii di codificazione, rispettivamente.

Per esempiu, u cumandamentu

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

compilerà u schedariu /usr/share/i18n/locales/ru_RU usendu u schedariu di codificazione /usr/share/i18n/charmaps/MAC-CYRILLIC.gz è salvà u risultatu in /usr/lib/locale/locale-archive sottu u nome ru_RU.maccyrillic

Se stabilisce a variabile LANG = en_US.UTF-8 allora glibc cercherà i binari locali in a seguente sequenza di schedari è cartulari:

/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 un locale si trova in formati tradiziunali è muderni, allora a priorità hè data à u mudernu.

Pudete vede a lista di locali compilati cù u cumandimu locale -a.

Preparate a vostra tabella di paragone

Avà, armatu cù a cunniscenza, pudete creà a vostra propria tabella di paragone di stringa ideale. Sta tavula deve paragunà currettamente e lettere russe, cumpresa a lettera Ё, è à u stessu tempu piglià in contu i segni di puntuazione in cunfurmità cù a tavola. ASCII.

U prucessu di preparà a vostra propria tavola di sorte hè custituita da duie tappe: edità a tavola di pesi è cumpilà in forma binaria cù u cumandimu. localdef.

Per esse a tavula di paragone per esse aghjustatu cù costi di edizione minima, in u formatu ISO 14652 Sezioni per aghjustà i pesi di una tavula esistente sò furnite. A seccione principia cù una keyword riordine-dopu è indicà a pusizioni dopu chì u rimpiazzamentu hè realizatu. A rùbbrica finisci cù a linea riordinu-fini. S'ellu hè necessariu di corriggerà parechje sezioni di a tavula, allora una sezione hè creata per ogni seccione.

Aghju copiatu novi versioni di i schedari iso14651_t1_common и ru_RU da u repository glibc à u mo cartulare di casa ~/.local/share/i18n/locales/ è aghju editatu un pocu a sezione LC_COLLATE в ru_RU. Novi versioni di i schedari sò cumplettamente compatible cù a mo versione glibc. Se vulete usà e versioni più vechje di i schedari, avete da cambià i nomi simbolichi è u locu induve a sustituzione principia in a tavula.

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

In fatti, saria bisognu di cambià i campi in LC_IDENTIFICAZIONE cusì chì puntanu à u locale ru_MY, ma in u mo esempiu ùn era micca necessariu, postu chì aghju esclusu l'archiviu da a ricerca di locali locale-archive.

chì localdef hà travagliatu cù i fugliali in u mo cartulare attraversu una variabile I18NPATH Pudete aghjunghje un repertoriu supplementu per circà i schedarii di input, è u repertoriu per salvà i schedarii binari pò esse specificatu cum'è una strada cù slashes:

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

POSIX assume chì in LINGUA pudete scrive percorsi assoluti à i cartulari cù i schedarii locali, cuminciendu cù una barra in avanti, ma glibc в Linux tutti i percorsi sò cuntati da u cartulare di basa, chì pò esse rimpiazzatu per una variàbile LOCPATH. Dopu à a stallazione LOCPATH=~/.local/lib/locale/ tutti i fugliali ligati à a localizazione seranu cercati solu in u mo cartulare. Archive di locali cù u set di variàbili LOCPATH ignoratu.

Eccu a prova decisiva:

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

Eura! Avemu fattu!

I travagli in bugs

Aghju digià rispostu à e dumande nantu à l'ordine di stringa presentate à u principiu, ma ci sò ancu parechje dumande nantu à l'errori - visibili è invisibili.

Riturnemu à u prublema originale.

È u prugramma magnusca è u prugramma Unete aduprate e stesse funzioni di paragone di stringa da glibc. Cumu hè accadutu chì Unete hà datu un errore di classificazione nantu à e fila ordinate da u cumandimu magnusca in locale fr_US.UTF-8? A risposta hè simplice: magnusca compara tutta a stringa, è Unete compara solu a chjave, chì per difettu hè u principiu di a stringa finu à u primu caratteru di spaziu biancu. In u mo esempiu, questu risultatu in un missaghju d'errore perchè l'ordine di e prime parolle in i linii ùn currisponde à l'ordine di e linee cumplette.

Locale "C" guarantisci chì in i strings ordinati i substrings iniziali finu à u primu spaziu seranu ancu urdinati, ma questu solu maschera l'errore. Hè pussibule selezziunà e dati (persone cù i stessi cognomi, ma nomi diffirenti) chì, senza u missaghju d'errore, daria un risultatu di fusione di file incorrecte. Se vulemu Unete fusioni di file file per nome cumpletu, allora u modu currettu seria di specificà esplicitamente u separatore di u campu è sorte per u campu chjave, è micca per tutta a linea. In questu casu, a fusione procederà currettamente è ùn ci sarà micca errore in ogni locale:

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

Esempiu eseguitu cù successu in codificazione CP1251 cuntene un altru errore. U fattu hè chì in tutte e distribuzioni cunnisciute da mè Linux i pacchetti mancanu locale compilatu ru_RU.CP1251. Se u locale compilatu ùn hè micca truvatu, allora magnusca usa in silenziu un paragone byte-by-byte, chì hè ciò chì avemu osservatu.

Per via, ci hè un altru picculu glitch ligatu à l'inaccessibilità di i locali compilati. squadra LOCPATH=/tmp locale -a darà una lista di tutti i locali in locale-archive, ma cù u settore variabile LOCPATH per tutti i prugrammi (cumpresu i più search) sti locali ùn saranu micca dispunibili.

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

cunchiusioni

Sè vo site un programatore chì hè abituatu à pensà chì e corde sò un set di bytes, allora a vostra scelta LC_COLLATE=C.

Sè vo site un compilatore di linguisti o di dizziunariu, allora hè megliu cumpilà in u vostru locale.

Sè vo site un usu sèmplice, allura vi basta à aduprà à u fattu chì u cumandamentu ls-a outputs i schedari chì cumincianu cù un puntu mischju cù i schedari chì cumincianu cù una lettera, è Comandante di mezzanotte, chì usa e so funzioni internu per sorte i nomi, mette i schedari chì cumincianu cù un puntu à u principiu di a lista.

referenze

Rapportu N ° 10 Algoritmu di colazione Unicode

Pesi di caratteri à unicode.org

ICU - implementazione di una biblioteca per travaglià cù Unicode da IBM.

Prova di classificazione utilizendu ICU

Pesi di caratteri in ISO 14651

Descrizzione di u furmatu di u schedariu cù scale ISO 14652

Discussione di paragone di stringa in glibc

Source: www.habr.com

Add a comment