{"id":83055,"date":"2020-05-28T01:42:15","date_gmt":"2020-05-27T23:42:15","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki"},"modified":"2020-05-28T01:42:15","modified_gmt":"2020-05-27T23:42:15","slug":"kak-linuxovskij-sort-sortiruet-stroki","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki","title":{"rendered":"Jak sortowa\u0107 wiersze w Linuxowym sort","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h1 id=\"vvedenie\">Wprowadzenie<\/h1>\n<p><\/p>\n<p>Wszystko zacz\u0119\u0142o si\u0119 od kr\u00f3tkiego skryptu, kt\u00f3ry mia\u0142 po\u0142\u0105czy\u0107 informacje o adresach <em>e-mail<\/em> pracownik\u00f3w, pozyskanych z listy u\u017cytkownik\u00f3w poczty masowej, z stanowiskami pracownik\u00f3w, uzyskanymi z bazy dzia\u0142u kadr. Obie listy zosta\u0142y wyeksportowane do plik\u00f3w tekstowych w kodowaniu Unicode <em>UTF-8<\/em> i zapisane z ko\u0144cami linii w stylu uniksowym.<\/p>\n<p><\/p>\n<p>Zawarto\u015b\u0107 <em>mail.txt<\/em><\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Iwanow Andriej;ia@example.com<\/code><\/pre>\n<p><\/p>\n<p>Zawarto\u015b\u0107 <em>buhg.txt<\/em><\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Iwanowa Alla;malarz\nJo\u0142kina Ella;d\u017awigowy\nIwanow Andriej;\u015blusarz\nAbakanow Michai\u0142;malarz<\/code><\/pre>\n<p><\/p>\n<p>Aby je po\u0142\u0105czy\u0107, pliki zosta\u0142y posortowane za pomoc\u0105 uniksowego polecenia <em>sort<\/em> i podane jako wej\u015bcie do programu uniksowego <em>join<\/em>, kt\u00f3ry niespodziewanie zako\u0144czy\u0142 si\u0119 b\u0142\u0119dem: <\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; sort buhg.txt &gt; buhg.srt\n$&gt; sort mail.txt &gt; mail.srt\n$&gt; join buhg.srt mail.srt &gt; result\njoin: buhg.srt:4: nie jest posortowane: Iwanow Andriej;\u015blusarz<\/code><\/pre>\n<p><\/p>\n<p>Przegl\u0105daj\u0105c wynik sortowania go\u0142ym okiem, mo\u017cna zauwa\u017cy\u0107, \u017ce sortowanie jest og\u00f3lnie poprawne, ale w przypadku pokrywaj\u0105cych si\u0119 nazwisk m\u0119skich i \u017ce\u0144skich, \u017ce\u0144skie s\u0105 przed m\u0119skimi:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; sort buhg.txt\nAbakanow Michai\u0142;malarz\nJo\u0142kina Ella;d\u017awigowy\nIwanowa Alla;malarz\nIwanow Andriej;\u015blusarz<\/code><\/pre>\n<p><\/p>\n<p>Wygl\u0105da to jak b\u0142\u0105d sortowania w Unicode lub jak przejaw feminizmu w algorytmie sortuj\u0105cym. Pierwsze jest, oczywi\u015bcie, bardziej prawdopodobne.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Odstawmy to na razie <em>join<\/em> i skupmy si\u0119 na <em>sort<\/em>. Spr\u00f3bujemy rozwi\u0105za\u0107 problem metod\u0105 pr\u00f3b i b\u0142\u0119d\u00f3w. Na pocz\u0105tek zmienimy lokalizacj\u0119 na <em>en_US<\/em> na <em>ru_RU<\/em>. Do sortowania wystarczy\u0142oby ustawi\u0107 zmienn\u0105 \u015brodowiskow\u0105 <em>LC_COLLATE<\/em>, ale nie b\u0119dziemy si\u0119 drobiazgowo ogranicza\u0107:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=ru_RU.UTF-8 sort buhg.txt\nAbakanow Michai\u0142;malarz\nJo\u0142kina Ella;d\u017awigowy\nIwanowa Alla;malarz\nIwanow Andriej;\u015blusarz<\/code><\/pre>\n<p><\/p>\n<p>Nic si\u0119 nie zmieni\u0142o.<\/p>\n<p><\/p>\n<p>Spr\u00f3bujemy przekonwertowa\u0107 pliki na jednobajtowe kodowanie: <\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; iconv -f UTF-8 -t KOI8-R buhg.txt \n | LANG=ru_RU.KOI8-R sort \n | iconv -f KOI8-R -t UTF8<\/code><\/pre>\n<p><\/p>\n<p>Zn\u00f3w nic si\u0119 nie zmienia.<\/p>\n<p><\/p>\n<p>Trudno, b\u0119dziemy musieli poszuka\u0107 rozwi\u0105zania w internecie. Bezpo\u015brednio o rosyjskich nazwiskach nic nie ma, ale s\u0105 pytania o inne dziwne przypadki sortowania. Oto na przyk\u0142ad taki problem: <noindex><a rel=\"nofollow\" href=\"https:\/\/serverfault.com\/questions\/95579\/unix-sort-treats-dash-characters-as-invisible\/95593\">unix sort traktuje znaki &#8216;-&#8216; (my\u015blnik) jako niewidoczne<\/a><\/noindex>. Kr\u00f3tko m\u00f3wi\u0105c, ci\u0105gi &quot;a-b&quot;, &quot;aa&quot;, &quot;ac&quot; s\u0105 sortowane jako &quot;aa&quot;, &quot;a-b&quot;, &quot;ac&quot;.<\/p>\n<p><\/p>\n<p>Odpowied\u017a wsz\u0119dzie jest standardowa: u\u017cyj lokalizacji programistycznej <em>&quot;C&quot;<\/em> i b\u0119dziesz szcz\u0119\u015bliwy. Spr\u00f3bujmy:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=C sort buhg.txt\nJo\u0142kina Ella;d\u017awigowy\nAbakanow Michai\u0142;malarz\nIwanow Andriej;\u015blusarz\nIwanowa Alla;adwokat<\/code><\/pre>\n<p><\/p>\n<p>Co\u015b si\u0119 zmieni\u0142o. Iwanowowie ustawili si\u0119 w poprawnej kolejno\u015bci, chocia\u017c Jo\u0142kina gdzie\u015b znikn\u0119\u0142a. Wracamy do pierwotnego zadania:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=C sort buhg.txt &gt; buhg.srt\n$&gt; LANG=C sort mail.txt &gt; mail.srt\n$&gt; LANG=C join buhg.srt mail.srt &gt; result<\/code><\/pre>\n<p><\/p>\n<p>Zadzia\u0142a\u0142o bez b\u0142\u0119d\u00f3w, tak jak obieca\u0142 Internet. I to mimo obecno\u015bci '\u0401\u043bki' w pierwszym wierszu.<\/p>\n<p><\/p>\n<p>Problem wydaje si\u0119 rozwi\u0105zany, ale na wszelki wypadek spr\u00f3bujemy jeszcze jedn\u0105 rosyjsk\u0105 kodowanie \u2014 windowsowe. <em>CP1251<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; iconv -f UTF-8 -t CP1251 buhg.txt \n | LANG=ru_RU.CP1251 sort \n | iconv -f CP1251 -t UTF8 <\/code><\/pre>\n<p><\/p>\n<p>Wynik sortowania, co dziwne, b\u0119dzie zgodny z lokalizacj\u0105. <em>&quot;C&quot;<\/em>, i ca\u0142y przyk\u0142ad, odpowiednio, przechodzi bez b\u0142\u0119d\u00f3w. To jaka\u015b mistyka.<\/p>\n<p><\/p>\n<p>Nie lubi\u0119 mistyki w programowaniu, poniewa\u017c zwykle maskuje ona b\u0142\u0119dy. B\u0119d\u0119 musia\u0142 na powa\u017cnie zaj\u0105\u0107 si\u0119 kwesti\u0105, jak to dzia\u0142a <em>sort<\/em> i na co wp\u0142ywa <em>LC_COLLATE<\/em> .<\/p>\n<p><\/p>\n<p>Na koniec postaram si\u0119 odpowiedzie\u0107 na pytania:<\/p>\n<p><\/p>\n<ul>\n<li>dlaczego b\u0142\u0119dnie sortowa\u0142y si\u0119 \u017ce\u0144skie nazwiska<\/li>\n<li>dlaczego <em>LANG=ru_RU.CP1251<\/em> okaza\u0142 si\u0119 r\u00f3wnowa\u017cny <em>LANG=C<\/em><\/li>\n<li>dlaczego maj\u0105 <em>sort<\/em> i <em>join<\/em> r\u00f3\u017cne reprezentacje porz\u0105dku posortowanych wierszy<\/li>\n<li>dlaczego we wszystkich moich przyk\u0142adach s\u0105 b\u0142\u0119dy<\/li>\n<li>na ko\u0144cu, jak sortowa\u0107 wiersze wed\u0142ug w\u0142asnego gustu<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"sortirovka-v-yunikode\">Sortowanie w Unicode<\/h1>\n<p><\/p>\n<p>Pierwszym przystankiem b\u0119dzie raport techniczny nr 10 pod tytu\u0142em <noindex><a rel=\"nofollow\" href=\"https:\/\/unicode.org\/reports\/tr10\/\">Unicode collation algorithm<\/a><\/noindex> na stronie <noindex><a rel=\"nofollow\" href=\"https:\/\/unicode.org\">unicode.org<\/a><\/noindex>. Raport zawiera wiele szczeg\u00f3\u0142\u00f3w technicznych, wi\u0119c pozwol\u0119 sobie na kr\u00f3tkie streszczenie g\u0142\u00f3wnych idei.<\/p>\n<p><\/p>\n<p><em>Collation<\/em> \u2014 &quot;por\u00f3wnanie&quot; ci\u0105g\u00f3w jest podstaw\u0105 ka\u017cdego algorytmu sortowania. Same algorytmy mog\u0105 si\u0119 r\u00f3\u017cni\u0107 (&quot;b\u0105belkowym&quot;, &quot;scalaj\u0105cym&quot;, &quot;szybkim&quot;), ale wszystkie b\u0119d\u0105 u\u017cywa\u0107 por\u00f3wnania pary ci\u0105g\u00f3w, aby okre\u015bli\u0107 ich kolejno\u015b\u0107.<\/p>\n<p><\/p>\n<p>Sortowanie wierszy w naturalnym j\u0119zyku to do\u015b\u0107 trudny problem. Nawet w najprostszym kodowaniu jednowymiarowym kolejno\u015b\u0107 liter w alfabecie, r\u00f3\u017cni\u0105ca si\u0119 od angielskiego, nie b\u0119dzie odpowiada\u0107 kolejno\u015bci warto\u015bci numerycznych, kt\u00f3rymi te litery s\u0105 kodowane. Tak w niemieckim alfabecie litera <em>\u00d6<\/em> znajduje si\u0119 pomi\u0119dzy <em>o<\/em> i <em>P<\/em>, a w kodowaniu <em>CP850<\/em> ona wpada pomi\u0119dzy <em>\u00ff<\/em> i <em>\u00dc<\/em>.<\/p>\n<p><\/p>\n<p>Mo\u017cna spr\u00f3bowa\u0107 abstrahowa\u0107 od konkretnego kodowania i postrzega\u0107 &quot;idealne&quot; litery, kt\u00f3re s\u0105 uporz\u0105dkowane w pewnym porz\u0105dku, jak to ma miejsce w Unicode. Kodowania <em>UTF8<\/em>, <em>UTF16<\/em> lub jednowymiarowe <em>KOI8-R<\/em> (je\u015bli potrzebne jest ograniczone podzbi\u00f3r Unicode) b\u0119d\u0105 mia\u0142y r\u00f3\u017cne numeryczne przedstawienia liter, ale b\u0119d\u0105 odnosi\u0107 si\u0119 do tych samych element\u00f3w podstawowej tabeli. <\/p>\n<p><\/p>\n<p>Okazuje si\u0119, \u017ce nawet buduj\u0105c tabel\u0119 znak\u00f3w od podstaw, nie b\u0119dziemy w stanie ustali\u0107 uniwersalnego porz\u0105dku znak\u00f3w. W r\u00f3\u017cnych krajowych alfabetach, kt\u00f3re u\u017cywaj\u0105 tych samych liter, kolejno\u015b\u0107 tych liter mo\u017ce si\u0119 r\u00f3\u017cni\u0107. Na przyk\u0142ad w j\u0119zyku francuskim <em>\u00c6<\/em> b\u0119dzie uwa\u017cana za ligatur\u0119 i sortowana jak ci\u0105g <em>AE<\/em>. W j\u0119zyku norweskim <em>\u00c6<\/em> b\u0119dzie oddzieln\u0105 liter\u0105, kt\u00f3ra znajduje si\u0119 po <em>Z<\/em>. Swoj\u0105 drog\u0105, opr\u00f3cz ligatur typu <em>\u00c6<\/em> istniej\u0105 litery, kt\u00f3re s\u0105 zapisywane za pomoc\u0105 kilku znak\u00f3w. Tak w alfabecie czeskim znajduje si\u0119 litera <em>Ch<\/em>, kt\u00f3ra stoi pomi\u0119dzy <em>H<\/em> i <em>I<\/em>.<\/p>\n<p><\/p>\n<p>. Opr\u00f3cz r\u00f3\u017cnic w alfabetach istniej\u0105 tak\u017ce inne narodowe tradycje, kt\u00f3re wp\u0142ywaj\u0105 na sortowanie. W szczeg\u00f3lno\u015bci pojawia si\u0119 pytanie: w jakiej kolejno\u015bci w s\u0142owniku powinny wyst\u0119powa\u0107 s\u0142owa z du\u017cych i ma\u0142ych liter? Wa\u017cnym aspektem mog\u0105 by\u0107 tak\u017ce zasady u\u017cycia znak\u00f3w przestankowych. W j\u0119zyku hiszpa\u0144skim na pocz\u0105tku zdania pytaj\u0105cego stawia si\u0119 odwr\u00f3cony znak zapytania (<em>\u00bfTe gusta la m\u00fasica?<\/em>). W tym przypadku oczywiste jest, \u017ce zdania pytaj\u0105ce nie powinny by\u0107 grupowane w oddzielny klaster poza alfabetem, a jak nale\u017cy sortowa\u0107 ci\u0105gi z innymi znakami przestankowymi?<\/p>\n<p><\/p>\n<p>Nie b\u0119d\u0119 si\u0119 zatrzymywa\u0142 na sortowaniu ci\u0105g\u00f3w w j\u0119zykach znacznie r\u00f3\u017cni\u0105cych si\u0119 od europejskich. Zaznaczam, \u017ce w j\u0119zykach z kierunkiem pisania od prawej do lewej lub z g\u00f3ry na d\u00f3\u0142 znaki w ci\u0105gach s\u0105 prawdopodobnie przechowywane w kolejno\u015bci czytania, a nawet w pi\u015bmiennictwach niealfabetowych istniej\u0105 sposoby porz\u0105dkowania znak\u00f3w. Na przyk\u0142ad, hieroglify mog\u0105 by\u0107 porz\u0105dkowane wed\u0142ug kszta\u0142tu (<noindex><a rel=\"nofollow\" href=\"https:\/\/studychinese.ru\/kljuchi\/\">klucze chi\u0144skich hieroglif\u00f3w<\/a><\/noindex>) lub wed\u0142ug wymowy. Jak nale\u017cy porz\u0105dkowa\u0107 emoji, szczerze m\u00f3wi\u0105c, nie mam poj\u0119cia, ale tak\u017ce dla nich mo\u017cna co\u015b wymy\u015bli\u0107.<\/p>\n<p><\/p>\n<p>Na podstawie wy\u017cej wymienionych cech sformu\u0142owano podstawowe wymagania dotycz\u0105ce por\u00f3wnywania ci\u0105g\u00f3w opartych na tabelach Unicode:<\/p>\n<p><\/p>\n<ul>\n<li>por\u00f3wnanie ci\u0105g\u00f3w nie zale\u017cy od pozycji symboli w tabeli kod\u00f3w;<\/li>\n<li>sekwencje symboli, tworz\u0105ce jeden symbol, s\u0105 sprowadzane do postaci kanonicznej (<em>A<\/em> + g\u00f3rne k\u00f3\u0142ko to to samo, co <em>\u00c5<\/em>);<\/li>\n<li>podczas por\u00f3wnywania ci\u0105g\u00f3w symbol jest rozpatrywany w kontek\u015bcie ci\u0105gu i, w razie potrzeby, \u0142\u0105czony z s\u0105siadami w jedn\u0105 jednostk\u0119 por\u00f3wnawcz\u0105 (<em>Ch<\/em> w czeskim) lub dzielony na kilka (<em>\u00c6<\/em> w francuskim);<\/li>\n<li>wszystkie cechy narodowe (alfabet, wielkie\/ma\u0142e litery, znaki interpunkcyjne, porz\u0105dek rodzaj\u00f3w pisma) powinny by\u0107 konfigurowane a\u017c do r\u0119cznego ustawienia porz\u0105dku (emoji);<\/li>\n<li>por\u00f3wnanie jest wa\u017cne nie tylko dla sortowania, ale tak\u017ce w wielu innych miejscach, na przyk\u0142ad do okre\u015blenia zakres\u00f3w wierszy (substytucja {A\u2026 \u044f} w <em>bash<\/em>);<\/li>\n<li>por\u00f3wnanie powinno by\u0107 wystarczaj\u0105co szybkie.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ponadto autorzy raportu sformu\u0142owali cechy por\u00f3wnania, na kt\u00f3re deweloperzy algorytm\u00f3w nie powinni polega\u0107:<\/p>\n<p><\/p>\n<ul>\n<li>algorytm por\u00f3wnania nie powinien wymaga\u0107 oddzielnego zestawu znak\u00f3w dla ka\u017cdego j\u0119zyka (j\u0119zyki rosyjski i ukrai\u0144ski wsp\u00f3lnie u\u017cywaj\u0105 wi\u0119kszo\u015bci znak\u00f3w cyrylickich);<\/li>\n<li>por\u00f3wnanie nie powinno opiera\u0107 si\u0119 na porz\u0105dku znak\u00f3w w tabelach Unicode;<\/li>\n<li>waga ci\u0105gu nie powinna by\u0107 atrybutem ci\u0105gu, poniewa\u017c ten sam ci\u0105g w r\u00f3\u017cnych kontekstach kulturowych mo\u017ce mie\u0107 r\u00f3\u017cne wagi;<\/li>\n<li>wagi ci\u0105g\u00f3w mog\u0105 si\u0119 zmienia\u0107 podczas scalania lub rozdzielania (z <em>x<\/em> &lt; <em>y<\/em> nie oznacza, \u017ce <em>xz<\/em> &lt; <em>yz<\/em>);<\/li>\n<li>r\u00f3\u017cne ci\u0105gi, kt\u00f3re maj\u0105 te same wagi, s\u0105 uwa\u017cane za r\u00f3wne z punktu widzenia algorytmu sortowania. Wprowadzenie dodatkowego porz\u0105dku takich ci\u0105g\u00f3w jest mo\u017cliwe, ale mo\u017ce pogorszy\u0107 wydajno\u015b\u0107;<\/li>\n<li>podczas kolejnych sortowa\u0144 ci\u0105gi, kt\u00f3re maj\u0105 te same wagi, mog\u0105 zmienia\u0107 miejsca. Stabilno\u015b\u0107 to cecha konkretnego algorytmu sortowania, a nie cecha algorytmu por\u00f3wnania ci\u0105g\u00f3w (zob. poprzedni punkt);<\/li>\n<li>zasady sortowania mog\u0105 si\u0119 zmienia\u0107 z czasem w miar\u0119 precyzowania\/zmiany tradycji kulturowych.<\/li>\n<\/ul>\n<p><\/p>\n<p>R\u00f3wnie\u017c zaznaczone, \u017ce algorytm por\u00f3wnania nic nie wie o semantyce przetwarzanych ci\u0105g\u00f3w. Tak wi\u0119c ci\u0105gi sk\u0142adaj\u0105ce si\u0119 wy\u0142\u0105cznie z cyfr nie powinny by\u0107 por\u00f3wnywane jako liczby, a w listach angielskich nazw nie powinno by\u0107 usuwane artyku\u0142 (<em>Beatles, The<\/em>).<\/p>\n<p><\/p>\n<p>Aby spe\u0142ni\u0107 wszystkie wymienione wymagania, zaproponowano wielopoziomowy (w rzeczywisto\u015bci czteropoziomowy) algorytm tablicowy do sortowania.<\/p>\n<p><\/p>\n<p>Najpierw znaki w ci\u0105gu s\u0105 przekszta\u0142cane do formy kanonicznej i grupowane w jednostki por\u00f3wnawcze. Ka\u017cdej jednostce por\u00f3wnawczej przypisuje si\u0119 kilka wag odpowiadaj\u0105cych kilku poziomom por\u00f3wnania. Wagi jednostek por\u00f3wnawczych to elementy uporz\u0105dkowanych zbior\u00f3w (w tym przypadku liczby ca\u0142kowite), kt\u00f3re mo\u017cna por\u00f3wnywa\u0107 w odniesieniu do wi\u0119kszych\/mniejszych. Specjalna warto\u015b\u0107 <em>IGNORED<\/em> (0x0) oznacza, \u017ce na danym poziomie por\u00f3wnania, ta jednostka nie bierze udzia\u0142u w por\u00f3wnaniu. Por\u00f3wnanie ci\u0105g\u00f3w mo\u017ce powtarza\u0107 si\u0119 kilka razy, z wykorzystaniem wag odpowiednich poziom\u00f3w. Na ka\u017cdym z poziom\u00f3w wagi jednostek por\u00f3wnania dw\u00f3ch ci\u0105g\u00f3w s\u0105 kolejno por\u00f3wnywane ze sob\u0105.<\/p>\n<p><\/p>\n<p>W r\u00f3\u017cnych realizacjach algorytmu dla r\u00f3\u017cnych krajowych tradycji warto\u015bci wsp\u00f3\u0142czynnik\u00f3w mog\u0105 si\u0119 r\u00f3\u017cni\u0107, ale w sk\u0142ad standardu Unicode wchodzi podstawowa tabela wag \u2014 <em>&quot;Domy\u015blna tabela element\u00f3w sortowania Unicode&quot;<\/em> (<em>DUCET<\/em>). Chc\u0119 zauwa\u017cy\u0107, \u017ce ustawienie zmiennej <em>LC_COLLATE<\/em> faktycznie polega na wskazaniu tabeli wag w funkcji por\u00f3wnania ci\u0105g\u00f3w.<\/p>\n<p><\/p>\n<p>Wsp\u00f3\u0142czynniki wag <em>DUCET<\/em> s\u0105 zorganizowane w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<p><\/p>\n<ul>\n<li>na pierwszym poziomie wszystkie litery s\u0105 sprowadzane do jednego rejestru, znaki diakrytyczne s\u0105 odrzucane, znaki interpunkcyjne (nie wszystkie) s\u0105 ignorowane;<\/li>\n<li>na drugim poziomie uwzgl\u0119dniane s\u0105 tylko znaki diakrytyczne;<\/li>\n<li>na trzecim poziomie uwzgl\u0119dniany jest tylko rejestr;<\/li>\n<li>na czwartym poziomie uwzgl\u0119dniane s\u0105 tylko znaki interpunkcyjne.<\/li>\n<\/ul>\n<p><\/p>\n<p>Por\u00f3wnanie odbywa si\u0119 w kilku przebiegach: najpierw por\u00f3wnywane s\u0105 wsp\u00f3\u0142czynniki pierwszego poziomu; je\u015bli wagi si\u0119 zgadzaj\u0105, przeprowadza si\u0119 ponowne por\u00f3wnanie z wagami drugiego poziomu; nast\u0119pnie, by\u0107 mo\u017ce, trzeciego i czwartego.<\/p>\n<p><\/p>\n<p>Por\u00f3wnanie ko\u0144czy si\u0119, gdy w ci\u0105gach znajduj\u0105 si\u0119 odpowiadaj\u0105ce sobie jednostki por\u00f3wnania o r\u00f3\u017cnych wagach. Ci\u0105gi, kt\u00f3re maj\u0105 r\u00f3wne wagi na wszystkich czterech poziomach, s\u0105 uwa\u017cane za r\u00f3wne sobie.<\/p>\n<p><\/p>\n<p>To w\u0142a\u015bnie ten algorytm (z mn\u00f3stwem dodatkowych szczeg\u00f3\u0142\u00f3w technicznych) nada\u0142 nazw\u0119 raportowi nr 10 \u2014 <em>&quot;Algorytm sortowania Unicode&quot;<\/em> (<em>UCA<\/em>).<\/p>\n<p><\/p>\n<p>W tym miejscu zachowanie sortowania z naszego przyk\u0142adu staje si\u0119 nieco bardziej zrozumia\u0142e. Dobrze by\u0142oby je por\u00f3wna\u0107 ze standardem Unicode.<\/p>\n<p><\/p>\n<p>Do testowania realizacji <em>UCA<\/em> istnieje specjalny <noindex><a rel=\"nofollow\" href=\"https:\/\/www.unicode.org\/Public\/UCA\/latest\/CollationTest.html\">test<\/a><\/noindex>, wykorzystuj\u0105cy <noindex><a rel=\"nofollow\" href=\"http:\/\/www.unicode.org\/Public\/UCA\/latest\/allkeys.txt\">plik wag<\/a><\/noindex>, wdra\u017caj\u0105cy <em>DUCET<\/em>. W pliku wag mo\u017cna znale\u017a\u0107 r\u00f3\u017cne ciekawostki. Na przyk\u0142ad, tam jest kolejno\u015b\u0107 ko\u015bci mahjonga i europejskiego domino, a tak\u017ce kolejno\u015b\u0107 kolor\u00f3w w talii kart (symbol <em>1F000<\/em> i dalej). Kolory kart s\u0105 uporz\u0105dkowane wed\u0142ug zasad brid\u017ca \u2014 PCHBT, a karty w kolorze \u2014 w kolejno\u015bci T, 2, 3\u2026 K.<\/p>\n<p><\/p>\n<p>R\u0119czne sprawdzenie poprawno\u015bci sortowania ci\u0105g\u00f3w zgodnie z <em>DUCET<\/em> by\u0142oby do\u015b\u0107 m\u0119cz\u0105ce, ale na szcz\u0119\u015bcie dla nas istnieje wzorcowa implementacja biblioteki do pracy z Unicode \u2014 &quot;<noindex><a rel=\"nofollow\" href=\"http:\/\/site.icu-project.org\/\">International Components for Unicode<\/a><\/noindex>&quot; (<em>ICU<\/em>).<\/p>\n<p><\/p>\n<p>Na stronie tej biblioteki, opracowanej w <em>IBM<\/em>, s\u0105 dost\u0119pne strony demonstracyjne, w tym <noindex><a rel=\"nofollow\" href=\"http:\/\/demo.icu-project.org\/icu-bin\/collation.html\">strona algorytmu por\u00f3wnywania ci\u0105g\u00f3w<\/a><\/noindex>. Wprowadzamy nasze testowe ci\u0105gi z ustawieniami domy\u015blnymi i, o cudzie, otrzymujemy idealne rosyjskie sortowanie.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Abakanow Micha\u0142;malarz\nJo\u0142kina Ella;operator \u017curawia\nIwanow Andriej;\u015blusarz\nIwanowa Alla;adwokat<\/code><\/pre>\n<p><\/p>\n<p>Przy okazji, na stronie <em>ICU<\/em> mo\u017cna znale\u017a\u0107 szczeg\u00f3\u0142owe informacje dotycz\u0105ce dzia\u0142ania algorytmu por\u00f3wnywania przy przetwarzaniu znak\u00f3w interpunkcyjnych. W przyk\u0142adach <noindex><a rel=\"nofollow\" href=\"http:\/\/userguide.icu-project.org\/collation\/faq\">Collation FAQ<\/a><\/noindex> ignorowane s\u0105 apostrof i my\u015blnik.<\/p>\n<p><\/p>\n<p>Unicode nam pom\u00f3g\u0142, ale przyczyny dziwnego zachowania <em>sort<\/em> do <em>Linuxa<\/em> trzeba b\u0119dzie szuka\u0107 gdzie indziej.<\/p>\n<p><\/p>\n<h1 id=\"sortirovka-v-glibc\">Sortowanie w glibc<\/h1>\n<p><\/p>\n<p>Szybki przegl\u0105d kodu \u017ar\u00f3d\u0142owego narz\u0119dzia <em>sort<\/em> z <em>GNU Core Utils<\/em> pokaza\u0142, \u017ce w samym narz\u0119dziu lokalizacja sprowadza si\u0119 do wy\u015bwietlania aktualnej warto\u015bci zmiennej <em>LC_COLLATE<\/em> przy uruchomieniu w trybie debugowania:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$ sort --debug buhg.txt &gt; buhg.srt\nsort: u\u017cywa regu\u0142 sortowania \u2018en_US.UTF8\u2019<\/code><\/pre>\n<p><\/p>\n<p>Por\u00f3wnanie ci\u0105g\u00f3w odbywa si\u0119 za pomoc\u0105 standardowej funkcji <em>strcoll<\/em>, a wi\u0119c wszystko interesuj\u0105ce znajduje si\u0119 w bibliotece <em>glibc<\/em>.<\/p>\n<p><\/p>\n<p>Na <em>wiki<\/em> projektu <em>glibc<\/em> po\u015bwi\u0119cony por\u00f3wnywaniu ci\u0105g\u00f3w <noindex><a rel=\"nofollow\" href=\"https:\/\/sourceware.org\/glibc\/wiki\/Locales#LC_COLLATE\">jeden akapit<\/a><\/noindex>. Z tego akapitu mo\u017cna zrozumie\u0107, \u017ce w <em>glibc<\/em> sortowanie opiera si\u0119 na nam ju\u017c znanym algorytmie <em>UCA<\/em> (<em>The Unicode collation algorithm<\/em>) i\/lub na zbli\u017conym standardzie <em>ISO 14651<\/em> (<em>Mi\u0119dzynarodowe porz\u0105dkowanie i por\u00f3wnywanie ci\u0105g\u00f3w<\/em>). Co do ostatniego standardu, nale\u017cy zauwa\u017cy\u0107, \u017ce na stronie <noindex><a rel=\"nofollow\" href=\"https:\/\/standards.iso.org\/ittf\/PubliclyAvailableStandards\">standards.iso.org<\/a><\/noindex> <em>ISO 14651<\/em> oficjalnie og\u0142oszonej jako publicznie dost\u0119pnej, jednak odpowiedni link prowadzi na nieistniej\u0105c\u0105 stron\u0119. Google wy\u015bwietla kilka stron z linkami do oficjalnych stron, kt\u00f3re oferuj\u0105 zakup elektronicznej kopii standardu za sto euro, ale na trzeciej-czwartej stronie wynik\u00f3w wyszukiwania mo\u017cna znale\u017a\u0107 tak\u017ce bezpo\u015brednie linki do <em>PDF<\/em>. Generalnie standard niewiele r\u00f3\u017cni si\u0119 od <em>UCA<\/em>, ale jest trudniejszy do przeczytania, poniewa\u017c nie zawiera \u017cywych przyk\u0142ad\u00f3w narodowych cech sortowania ci\u0105g\u00f3w. <\/p>\n<p><\/p>\n<p>Najciekawsz\u0105 informacj\u0105 na <em>wiki<\/em> okaza\u0142 si\u0119 link do <noindex><a rel=\"nofollow\" href=\"https:\/\/sourceware.org\/bugzilla\/show_bug.cgi?id=14095\">\u015bledzenia b\u0142\u0119d\u00f3w<\/a><\/noindex> z dyskusj\u0105 na temat implementacji por\u00f3wnywania ci\u0105g\u00f3w w <em>glibc<\/em>. Z dyskusji mo\u017cna dowiedzie\u0107 si\u0119, \u017ce w <em>glibc<\/em> do por\u00f3wnywania ci\u0105g\u00f3w u\u017cywa si\u0119 <em>ISO<\/em>tablicy <noindex><a rel=\"nofollow\" href=\"http:\/\/www.iso.org\/ittf\/ISO14651_2006_TABLE1_en.txt\">The Common Template Table<\/a><\/noindex> (<em>CTT<\/em>), adres kt\u00f3rej mo\u017cna znale\u017a\u0107 w za\u0142\u0105czniku <em>A<\/em> standardu <em>ISO 14651<\/em>. Mi\u0119dzy rokiem 2000 a 2015 ta tablica w <em>glibc<\/em> nie mia\u0142a maintainer'a i znacznie r\u00f3\u017cni\u0142a si\u0119 (przynajmniej zewn\u0119trznie) od obecnej wersji standardu. Od 2015 do 2018 roku trwa\u0142a adaptacja do nowej wersji tabeli i obecnie masz szans\u0119 spotka\u0107 w rzeczywisto\u015bci zar\u00f3wno now\u0105 wersj\u0119 tabeli (<em>CentOS 8<\/em>), jak i star\u0105 (<em>CentOS 7<\/em>). <\/p>\n<p><\/p>\n<p>Teraz, kiedy mamy wszystkie informacje o algorytmie i pomocniczych tabelach, mo\u017cemy wr\u00f3ci\u0107 do pierwotnego problemu i zrozumie\u0107, jak prawid\u0142owo sortowa\u0107 wiersze w rosyjskiej lokalizacji.<\/p>\n<p><\/p>\n<h1 id=\"iso-1465114652\">ISO 14651\/14652<\/h1>\n<p><\/p>\n<p>Kod \u017ar\u00f3d\u0142owy interesuj\u0105cej nas tabeli <em>CTT<\/em> w wi\u0119kszo\u015bci dystrybucji <em>Linuxa<\/em> znajduje si\u0119 w katalogu <em>\/usr\/share\/i18n\/locales\/<\/em>. Sama tabela znajduje si\u0119 w pliku <em>iso14651_t1_common<\/em>. Nast\u0119pnie ten plik za pomoc\u0105 dyrektywy <em>copy iso14651_t1_common<\/em> jest w\u0142\u0105czany do pliku <em>iso14651_t1<\/em>, kt\u00f3ry z kolei jest w\u0142\u0105czany do plik\u00f3w narodowych, w tym do <em>en_US<\/em> i <em>ru_RU<\/em>. W wi\u0119kszo\u015bci dystrybucji <em>Linuxa<\/em> wszystkie pliki \u017ar\u00f3d\u0142owe s\u0105 do\u0142\u0105czone do podstawowej instalacji, ale je\u015bli ich nie ma, trzeba b\u0119dzie zainstalowa\u0107 dodatkowy pakiet z dystrybucji.<\/p>\n<p><\/p>\n<p>Struktura pliku <em>iso14651_t1<\/em> mo\u017ce wydawa\u0107 si\u0119 okropnie rozwlek\u0142a, z nieoczywistymi zasadami budowy nazw, ale je\u015bli si\u0119 przyjrze\u0107, to wszystko jest do\u015b\u0107 proste. Struktura jest opisana w standardzie <em>ISO 14652<\/em>, kt\u00f3rego kopi\u0119 mo\u017cna pobra\u0107 ze strony <noindex><a rel=\"nofollow\" href=\"http:\/\/www.open-std.org\/JTC1\/SC22\/WG20\/docs\/n972-14652ft.pdf\">open-std.org<\/a><\/noindex>. Kolejny opis formatu pliku mo\u017cna przeczyta\u0107 w <noindex><a rel=\"nofollow\" href=\"https:\/\/pubs.opengroup.org\/onlinepubs\/9699919799\/basedefs\/V1_chap07.html\">specyfikacjach<\/a><\/noindex> <em>POSIX<\/em> od <em>OpenGroup<\/em>. Alternatywnie, zamiast czyta\u0107 standard, mo\u017cna zapozna\u0107 si\u0119 z kodami \u017ar\u00f3d\u0142owymi funkcji <em>collate_read<\/em> do <em>glibc\/locale\/programs\/ld-collate.c<\/em>.<\/p>\n<p><\/p>\n<p>Struktura pliku wygl\u0105da nast\u0119puj\u0105co:<\/p>\n<p><\/p>\n<p>Domy\u015blnie, symbol  jest u\u017cywany jako symbol escape, a koniec linii po symbolu # jest komentarzem. Oba symbole mo\u017cna nadpisa\u0107, co zosta\u0142o zrobione w nowej wersji tabeli:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">escape_char \/\ncomment_char %<\/code><\/pre>\n<p><\/p>\n<p>W pliku b\u0119d\u0105 wyst\u0119powa\u0107 tokeny w formacie <em>&lt;Uxxxx&gt;<\/em> lub <em>&lt;Uxxxxxxxx&gt;<\/em> (gdzie <em>x<\/em> \u2014 szesnastkowa cyfra). To szesnastkowe przedstawienie punkt\u00f3w kodowych Unicode w kodowaniu <em>UCS-4<\/em> (<em>UTF-32<\/em>). Wszystkie pozosta\u0142e elementy w nawiasach k\u0105towych (w tym <em>&lt;Uxxxx_xxxx&gt;<\/em>, <em>&lt;2&gt;<\/em> i tym podobne), s\u0105 traktowane jako proste sta\u0142e tekstowe, kt\u00f3re nie maj\u0105 szczeg\u00f3lnego sensu poza kontekstem.<\/p>\n<p><\/p>\n<p>Ci\u0105g <em>LC_COLLATE<\/em> m\u00f3wi nam, \u017ce dalej zaczynaj\u0105 si\u0119 dane opisuj\u0105ce por\u00f3wnanie wierszy.<\/p>\n<p><\/p>\n<p>Najpierw ustala si\u0119 nazwy dla wag w tabeli por\u00f3wnawczej oraz nazwy dla symboli kolacjowych. Og\u00f3lnie rzecz bior\u0105c, dwa rodzaje nazw nale\u017c\u0105 do dw\u00f3ch r\u00f3\u017cnych byt\u00f3w, ale w rzeczywistym pliku s\u0105 one pomieszane. Nazwy wag s\u0105 ustalane za pomoc\u0105 s\u0142owa kluczowego <em>collating-symbol<\/em> (symbol por\u00f3wnawczy), poniewa\u017c podczas por\u00f3wnywania symbole Unicode, kt\u00f3re maj\u0105 te same wagi, b\u0119d\u0105 uznawane za r\u00f3wnowa\u017cne symbole.<\/p>\n<p><\/p>\n<p>\u0141\u0105czna d\u0142ugo\u015b\u0107 sekcji w bie\u017c\u0105cej wersji pliku wynosi oko\u0142o 900 wierszy. Wybiera\u0142em przyk\u0142ady z r\u00f3\u017cnych miejsc, aby pokaza\u0107 dowolno\u015b\u0107 nazw i kilka rodzaj\u00f3w sk\u0142adni.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">LC_COLLATE\n\nsymbol sortuj\u0105cy &lt;RES-1&gt;\nsymbol sortuj\u0105cy &lt;BLK&gt;\nsymbol sortuj\u0105cy &lt;MIN&gt;\nsymbol sortuj\u0105cy &lt;WIDE&gt;\n...\nsymbol sortuj\u0105cy &lt;ARABIC&gt;\nsymbol sortuj\u0105cy &lt;ETHPC&gt;\nsymbol sortuj\u0105cy &lt;OSMANYA&gt;\n...\nsymbol sortuj\u0105cy &lt;S1D000&gt;..&lt;S1D35F&gt;\nsymbol sortuj\u0105cy &lt;SFFFF&gt; % Gwarantowana najwi\u0119ksza warto\u015b\u0107 symbolu. Zachowaj na ko\u0144cu tej listy\n...\nelement sortuj\u0105cy &lt;U0413_0301&gt; z &quot;&lt;U0413&gt;&lt;U0301&gt;&quot;\nelement sortuj\u0105cy &lt;U0413_0341&gt; z &quot;&lt;U0413&gt;&lt;U0341&gt;&quot;<\/code><\/pre>\n<p><\/p>\n<ul>\n<li><em>collating-symbol<\/em> rejestruje ci\u0105g <em>OSMANYA<\/em> w tabeli nazw wag <\/li>\n<li><em>collating-symbol ..<\/em> rejestruje sekwencj\u0119 nazw sk\u0142adaj\u0105c\u0105 si\u0119 z prefiksu <em>S<\/em> i szesnastkowego sufiksu od <em>1D000<\/em> do <em>1D35F<\/em>.<\/li>\n<li><em>FFFF<\/em> do <em>collating-symbol<\/em> wygl\u0105da jak du\u017ca liczba ca\u0142kowita bez znaku w systemie szesnastkowym, ale <em>&lt;SFFFF&gt;<\/em> to tylko nazwa, kt\u00f3ra mog\u0142aby wygl\u0105da\u0107 jak <em>&lt;VERYBIGVAL&gt;<\/em> <\/li>\n<li>nazwa <em>&lt;U0413&gt;<\/em> oznacza punkt kodowy w kodowaniu <em>UCS-4<\/em><\/li>\n<li><em>element sortuj\u0105cy &lt;U0413_0301&gt; z &quot;&lt;U0413&gt;&lt;U0301&gt;&quot;<\/em> rejestruje now\u0105 nazw\u0119 dla pary punkt\u00f3w Unicode. <\/li>\n<\/ul>\n<p><\/p>\n<p>Gdy wagi nazw s\u0105 okre\u015blone, ustala si\u0119 same wagi. Poniewa\u017c przy por\u00f3wnywaniu liczy si\u0119 tylko relacja wi\u0119ksze-mniejsze, wagi okre\u015blane s\u0105 prost\u0105 sekwencj\u0105 wypisania nazw. Najpierw wypisuje si\u0119 bardziej &quot;lekkie&quot; wagi, a nast\u0119pnie bardziej &quot;ci\u0119\u017ckie&quot;. Przypominam, \u017ce ka\u017cdemu symbolowi w Unicode przypisywane s\u0105 cztery r\u00f3\u017cne wagi. Tutaj zosta\u0142y one zebrane w jednolit\u0105 uporz\u0105dkowan\u0105 sekwencj\u0119. Teoretycznie, ka\u017cda nazwa symboliczna mo\u017ce by\u0107 u\u017cywana na kt\u00f3rymkolwiek z czterech poziom\u00f3w, ale komentarze wskazuj\u0105, \u017ce programi\u015bci my\u015blowo dziel\u0105 nazwy na poziomy.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">% Przypisania wag symbolicznych\n\n% Przypisania wag trzeciego poziomu\n\n\n\n\n...\n% Przypisania wag drugiego poziomu\n\n % \u0141\u0104CZ\u0104CA LINIA NISKA\n % \u0141\u0104CZ\u0104CA PRZECINEK NAD\n % \u0141\u0104CZ\u0104CA ODWR\u00d3CONA PRZECINEK NAD\n...\n% Przypisania wag pierwszego poziomu\n % HORYZONTALNE TABULACJE \n % ZNAK NOWEJ LINI\n % WERTYKALNE TABULACJE\n...\n % CYRYLICKA MA\u0141A LITERA DE\n % CYRYLICKA MA\u0141A LITERA KOMI DE\n % CYRYLICKA MA\u0141A LITERA DJE\n % CYRYLICKA MA\u0141A LITERA KOMI DJE\n % CYRYLICKA MA\u0141A LITERA GJE\n % CYRYLICKA MA\u0141A LITERA ZE Z ZST\u0118PNIKIEM\n % CYRYLICKA MA\u0141A LITERA IE\n % CYRYLICKA MA\u0141A LITERA IE Z BREVE\n % CYRYLICKA MA\u0141A LITERA UKRAI\u0143SKIE IE\n % CYRYLICKA MA\u0141A LITERA ZHE<\/code><\/pre>\n<p><\/p>\n<p>W ko\u0144cu sam tabela wag.<\/p>\n<p><\/p>\n<p>Sekcja wag jest zawarta w wierszach z s\u0142owami kluczowymi <em>order_start<\/em> i <em>order_end<\/em>. Dodatkowe parametry <em>order_start<\/em> okre\u015blaj\u0105, w kt\u00f3rym kierunku przegl\u0105dane s\u0105 wiersze na ka\u017cdym poziomie por\u00f3wnania. Domy\u015blnie u\u017cywa si\u0119 parametru <em>forward<\/em>. Cia\u0142o sekcji sk\u0142ada si\u0119 z wierszy, kt\u00f3re zawieraj\u0105 kod znaku i cztery jego wagi. Kod znaku mo\u017ce by\u0107 reprezentowany przez sam znak, kod punktowy lub symboliczne imi\u0119 zdefiniowane wcze\u015bniej. Wagi mog\u0105 by\u0107 r\u00f3wnie\u017c okre\u015blane przez symboliczne imiona, kodowe punkty lub sam\u0105 postaci\u0105. Je\u017celi u\u017cywane s\u0105 punkty kodowe lub znaki, to ich waga odpowiada warto\u015bci liczbowej punktu kodowego (pozycji w tabeli Unicode). Znaki, kt\u00f3re nie s\u0105 okre\u015blone w spos\u00f3b jawny (jak rozumiem) s\u0105 uwa\u017cane za przypisane w tabeli z wag\u0105 podstawow\u0105 odpowiadaj\u0105c\u0105 pozycji w tabeli Unicode. Specjalna warto\u015b\u0107 wagi <em>IGNORE<\/em> oznacza, \u017ce na odpowiednim poziomie por\u00f3wnania dany znak jest ignorowany.<\/p>\n<p><\/p>\n<p>Aby zilustrowa\u0107 struktur\u0119 wag, wybra\u0142em trzy do\u015b\u0107 oczywiste fragmenty:<\/p>\n<p><\/p>\n<ul>\n<li>znaki, kt\u00f3re s\u0105 ca\u0142kowicie ignorowane<\/li>\n<li>znaki odpowiadaj\u0105ce cyfrze trzy na pierwszych dw\u00f3ch poziomach<\/li>\n<li>pocz\u0105tek alfabetu cyrylickiego, kt\u00f3ry nie zawiera znak\u00f3w diakrytycznych, a zatem jest sortowany g\u0142\u00f3wnie wed\u0142ug pierwszego i trzeciego poziomu.<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"plaintext\">order_start forward;forward;forward;forward,position\n IGNORE;IGNORE;IGNORE;IGNORE % NULL (in 6429)\n IGNORE;IGNORE;IGNORE;IGNORE % START OF HEADING (in 6429)\n IGNORE;IGNORE;IGNORE;IGNORE % START OF TEXT (in 6429)\n...\n ;;; % DIGIT THREE\n ;;; % FULLWIDTH DIGIT THREE\n ;;; % PARENTHESIZED DIGIT THREE\n ;;; % DIGIT THREE FULL STOP\n ;;<FONT>; % MATHEMATICAL BOLD DIGIT THREE\n...\n ;;; % CYRILLIC SMALL LETTER A\n ;;; % CYRILLIC CAPITAL LETTER A\n ;;; % CYRILLIC SMALL LETTER A WITH BREVE\n ;;; % CYRILLIC SMALL LETTER A WITH BREVE\n...\n ;;; % CYRILLIC SMALL LETTER BE\n ;;; % CYRILLIC CAPITAL LETTER BE\n ;;; % CYRILLIC SMALL LETTER VE\n ;;; % CYRILLIC CAPITAL LETTER VE\n...\norder_end<\/code><\/pre>\n<p><\/p>\n<p>Mo\u017cna teraz ponownie wr\u00f3ci\u0107 do sortowania przyk\u0142ad\u00f3w z pocz\u0105tku artyku\u0142u. Pu\u0142apka kryje si\u0119 w tej cz\u0119\u015bci tabeli wag:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">IGNORE;IGNORE;IGNORE; % SPACE\n IGNORE;IGNORE;IGNORE; % EXCLAMATION MARK\n IGNORE;IGNORE;IGNORE; % QUOTATION MARK\n...<\/code><\/pre>\n<p><\/p>\n<p>Wida\u0107, \u017ce w tej tabeli znaki interpunkcyjne z tabeli <em>ASCII<\/em> (w tym spacja) podczas por\u00f3wnywania ci\u0105g\u00f3w jest niemal zawsze ignorowane. Wyj\u0105tek stanowi\u0105 tylko ci\u0105gi, kt\u00f3re s\u0105 identyczne pod wzgl\u0119dem wszystkich znak\u00f3w poza interpunkcj\u0105 na tych samych pozycjach. Ci\u0105gi z mojego przyk\u0142adu (po sortowaniu) wygl\u0105daj\u0105 dla algorytmu por\u00f3wnania w ten spos\u00f3b:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">AbakanowMicha\u0142Malierz\nJelkinaElkaRano\nIwanowaAllaMalierz\nIwanowAndrzej\u015alusarz<\/code><\/pre>\n<p><\/p>\n<p>Bior\u0105c pod uwag\u0119, \u017ce w tabeli wag wielkie litery w j\u0119zyku rosyjskim znajduj\u0105 si\u0119 po ma\u0142ych (na trzecim poziomie <em>&lt;CAP&gt;<\/em> ci\u0119\u017cszy ni\u017c <em>&lt;MIN&gt;<\/em>), sortowanie wygl\u0105da absolutnie poprawnie.<\/p>\n<p><\/p>\n<p>Podczas ustawiania zmiennej <em>LC_COLLATE=C<\/em> \u0142adowana jest specjalna tabela, kt\u00f3ra okre\u015bla por\u00f3wnanie bajt\u00f3w<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">static const uint32_t collseqwc[] =\n{\n  8, 1, 8, 0x0, 0xff,\n  \/* 1st-level table *\/\n  6 * sizeof (uint32_t),\n  \/* 2nd-level table *\/\n  7 * sizeof (uint32_t),\n  \/* 3rd-level table *\/\n  L'x00', L'x01', L'x02', L'x03', L'x04', L'x05', L'x06', L'x07',\n  L'x08', L'x09', L'x0a', L'x0b', L'x0c', L'x0d', L'x0e', L'x0f',\n\n...\n  L'xf8', L'xf9', L'xfa', L'xfb', L'xfc', L'xfd', L'fe', L'xff'\n};<\/code><\/pre>\n<p><\/p>\n<p>Poniewa\u017c w Unicode kodowa punktacja \u0401 znajduje si\u0119 przed A, ci\u0105gi s\u0105 sortowane odpowiednio.<\/p>\n<p><\/p>\n<h1 id=\"tekstovye-i-dvoichnye-tablicy\">Tablice tekstowe i binarne<\/h1>\n<p><\/p>\n<p>Jest oczywiste, \u017ce por\u00f3wnanie ci\u0105g\u00f3w to niezwykle cz\u0119sto spotykana operacja, a analiza tabeli <em>CTT<\/em> dosy\u0107 kosztowny proces. Aby zoptymalizowa\u0107 dost\u0119p do tabeli, kompilowana jest do postaci binarnej za pomoc\u0105 polecenia <em>localedef<\/em>.<\/p>\n<p><\/p>\n<p>Zesp\u00f3\u0142 <em>localedef<\/em> przyjmuje jako parametry plik z tabel\u0105 narodowych szczeg\u00f3\u0142\u00f3w (opcja <em>-i<\/em>), w kt\u00f3rym wszystkie znaki s\u0105 reprezentowane przez punkty Unicode, oraz plik mapowania punkt\u00f3w Unicode na znaki konkretnej kodowania (opcja <em>-f<\/em>). W wyniku dzia\u0142ania tworzone s\u0105 pliki binarne dla lokalizacji, o nazwie podanej w ostatnim parametrze.<\/p>\n<p><\/p>\n<p><em>Glibc<\/em> obs\u0142uguje dwa formaty plik\u00f3w binarnych: &quot;tradycyjny&quot; i &quot;nowoczesny&quot;.<\/p>\n<p><\/p>\n<p>Tradycyjny format zak\u0142ada, \u017ce nazwa lokalizacji to nazwa podkatalogu w <em>\/usr\/lib\/locale\/<\/em>. W tym podkatalogu przechowywane s\u0105 pliki binarne <em>LC_COLLATE<\/em>, <em>LC_CTYPE<\/em>, <em>LC_TIME<\/em> itd. Plik <em>LC_IDENTIFICATION<\/em> zawiera formaln\u0105 nazw\u0119 lokalizacji (kt\u00f3ra mo\u017ce r\u00f3\u017cni\u0107 si\u0119 od nazwy katalogu) oraz komentarze.<\/p>\n<p><\/p>\n<p>Nowoczesny format zak\u0142ada przechowywanie wszystkich lokalizacji w jednym archiwum <em>\/usr\/lib\/locale\/locale-archive<\/em>, kt\u00f3re jest mapowane do pami\u0119ci wirtualnej wszystkich proces\u00f3w korzystaj\u0105cych z <em>glibc<\/em>. Nazwa lokalizacji w nowoczesnym formacie podlega pewnej kanonizacji \u2014 w nazwach kodowania pozostaj\u0105 tylko cyfry i litery, przekszta\u0142cone na ma\u0142e litery. Tak <em>ru_RU.KOI8-R<\/em>, zostanie zachowana jako <em>ru_RU.koi8r<\/em>.<\/p>\n<p><\/p>\n<p>Pliki wej\u015bciowe s\u0105 poszukiwane w bie\u017c\u0105cym katalogu, a tak\u017ce w katalogach <em>\/usr\/share\/i18n\/locales\/<\/em> i <em>\/usr\/share\/i18n\/charmaps\/<\/em> dla plik\u00f3w <em>CTT<\/em> i plik\u00f3w kodowania odpowiednio.<\/p>\n<p><\/p>\n<p>Na przyk\u0142ad, polecenie<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">localedef -i ru_RU -f MAC-CYRILLIC ru_RU.MAC-CYRILLIC<\/code><\/pre>\n<p><\/p>\n<p>skompiluje plik <em>\/usr\/share\/i18n\/locales\/ru_RU<\/em> z wykorzystaniem pliku kodowania <em>\/usr\/share\/i18n\/charmaps\/MAC-CYRILLIC.gz<\/em> i zapisze wynik w <em>\/usr\/lib\/locale\/locale-archive<\/em> pod nazw\u0105 <em>ru_RU.maccyrillic<\/em><\/p>\n<p><\/p>\n<p>Je\u015bli ustawisz zmienn\u0105 <em>LANG=en_US.UTF-8<\/em> , to <em>glibc<\/em> b\u0119dzie szukacz pliki binarnych lokalizacji w nast\u0119puj\u0105cej kolejno\u015bci plik\u00f3w i katalog\u00f3w:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">\/usr\/lib\/locale\/locale-archive\n\/usr\/lib\/locale\/en_US.UTF-8\/\n\/usr\/lib\/locale\/en_US\/\n\/usr\/lib\/locale\/enUTF-8\/\n\/usr\/lib\/locale\/en\/<\/code><\/pre>\n<p><\/p>\n<p>Je\u015bli lokalizacja wyst\u0119puje zar\u00f3wno w tradycyjnym, jak i nowoczesnym formacie, pierwsze\u0144stwo ma format nowoczesny.<\/p>\n<p><\/p>\n<p>Lista skompilowanych lokalizacji mo\u017ce by\u0107 przegl\u0105dana za pomoc\u0105 polecenia <em>locale -a<\/em>.<\/p>\n<p><\/p>\n<h1 id=\"podgotovka-svoey-tablicy-sravneniya\">Przygotowanie w\u0142asnej tabeli por\u00f3wnawczej<\/h1>\n<p><\/p>\n<p>Teraz, uzbrojony w wiedz\u0119, mo\u017cesz stworzy\u0107 w\u0142asn\u0105 idealn\u0105 tabel\u0119 por\u00f3wnawcz\u0105 \u0142a\u0144cuch\u00f3w. Ta tabela powinna poprawnie por\u00f3wnywa\u0107 rosyjskie litery, w tym liter\u0119 \u0401, i przy tym uwzgl\u0119dnia\u0107 znaki przestankowe zgodnie z tabel\u0105 <em>ASCII<\/em>.<\/p>\n<p><\/p>\n<p>Proces przygotowania w\u0142asnej tabeli sortowania sk\u0142ada si\u0119 z dw\u00f3ch etap\u00f3w: edytowania tabeli wag i kompilacji jej w form\u0119 binarn\u0105 za pomoc\u0105 polecenia <em>localedef<\/em>.<\/p>\n<p><\/p>\n<p>Aby tabela por\u00f3wnawcza mog\u0142a by\u0107 dostosowywana przy minimalnych kosztach edycyjnych, w formacie <em>ISO 14652<\/em> zapewniaj\u0105 sekcje korekty wag istniej\u0105cej tabeli. Sekcja zaczyna si\u0119 od s\u0142owa kluczowego <em>reorder-after<\/em> i wskazania pozycji, po kt\u00f3rej nast\u0119puje zamiana. Sekcj\u0119 ko\u0144czy linia <em>reorder-end<\/em>. Je\u015bli konieczne jest poprawienie kilku fragment\u00f3w tabeli, tworzy si\u0119 osobn\u0105 sekcj\u0119 dla ka\u017cdego z takich fragment\u00f3w.<\/p>\n<p><\/p>\n<p>Skopiowa\u0142em nowe wersje plik\u00f3w <em>iso14651_t1_common<\/em> i <em>ru_RU<\/em> z repozytorium <em>glibc<\/em> do mojego katalogu domowego ~\/.local\/share\/i18n\/locales\/ i lekko edytowa\u0142em sekcj\u0119 <em>LC_COLLATE<\/em> do <em>ru_RU<\/em>. Nowe wersje plik\u00f3w s\u0105 w pe\u0142ni zgodne z moj\u0105 wersj\u0105 <em>glibc<\/em>. Je\u015bli chcesz u\u017cy\u0107 starych wersji plik\u00f3w, musisz zmieni\u0107 symboliczne nazwy oraz miejsce, od kt\u00f3rego rozpoczyna si\u0119 zamiana w tabeli.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">LC_COLLATE\n% Skopiuj szablon z ISO\/IEC 14651\nskopiuj &quot;iso14651_t1&quot;\npozycjonuj-po &lt;U000D&gt;\n&lt;U0020&gt; &lt;S0020&gt;;&lt;BASE&gt;;&lt;MIN&gt;&lt;U0020&gt; % SPACJA\n&lt;U0021&gt; &lt;S0021&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U0021&gt; % ZNAK WYKRZYKNIKA\n&lt;U0022&gt; &lt;S0022&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U0022&gt; % ZNAK CUDZYS\u0141OWA\n...\n&lt;U007D&gt; &lt;S007D&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U007D&gt; % PRAWA KLAMRA\n&lt;U007E&gt; &lt;S007E&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U007E&gt; % TYLDAT\npozycjonuj-koniec\nKONIEC LC_COLLATE<\/code><\/pre>\n<p><\/p>\n<p>W rzeczywisto\u015bci nale\u017ca\u0142oby zmieni\u0107 pola w <em>LC_IDENTIFICATION<\/em> tak aby wskazywa\u0142y na lokalizacj\u0119 <em>ru_MY<\/em>, ale w moim przyk\u0142adzie nie by\u0142o to konieczne, poniewa\u017c wykluczy\u0142em z wyszukiwania lokalizacji archiwum <em>locale-archive<\/em>.<\/p>\n<p><\/p>\n<p>Aby <em>localedef<\/em> pracowa\u0142em z plikami w swoim katalogu za po\u015brednictwem zmiennej <em>I18NPATH<\/em> mo\u017cna doda\u0107 dodatkowy katalog do wyszukiwania plik\u00f3w wej\u015bciowych, a katalog do zapisywania plik\u00f3w binarnych mo\u017cna okre\u015bli\u0107 jako \u015bcie\u017ck\u0119 z uko\u015bnikami:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; I18NPATH=~\/.local\/share\/i18n localedef -i ru_RU -f UTF-8 ~\/.local\/lib\/locale\/ru_MY.UTF-8<\/code><\/pre>\n<p><\/p>\n<p><em>POSIX<\/em> zak\u0142ada, \u017ce w <em>LANG<\/em> mo\u017cna pisa\u0107 absolutne \u015bcie\u017cki do katalog\u00f3w z plikami lokalizacyjnymi, zaczynaj\u0105c od uko\u015bnika, ale <em>glibc<\/em> do <em>Linuxa<\/em> wszystkie \u015bcie\u017cki s\u0105 liczone od katalogu bazowego, kt\u00f3ry mo\u017cna nadpisa\u0107 przez zmienn\u0105 <em>LOCPATH<\/em>. Po ustawieniu <em>LOCPATH=~\/.local\/lib\/locale\/<\/em> wszystkie pliki zwi\u0105zane z lokalizacj\u0105 b\u0119d\u0105 wyszukiwane tylko w moim katalogu. Archiwum lokalizacyjne przy ustawionej zmiennej <em>LOCPATH<\/em> jest ignorowane.<\/p>\n<p><\/p>\n<p>Oto on, decyduj\u0105cy test:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=ru_MY.UTF-8 LOCPATH=~\/.local\/lib\/locale\/ sort buhg.txt\nAbakanow Micha\u0142;malarz\nJo\u0142kina El\u017cbieta;operator d\u017awigu\nIwanow Andrzej; \u015blusarz\nIwanowa Alla;adwokat<\/code><\/pre>\n<p><\/p>\n<p>Hurra! Zrobili\u015bmy to!<\/p>\n<p><\/p>\n<h1 id=\"rabota-nad-oshibkami\">Praca nad b\u0142\u0119dami<\/h1>\n<p><\/p>\n<p>Ju\u017c odpowiedzia\u0142em na pytania dotycz\u0105ce sortowania linii, kt\u00f3re zadano na pocz\u0105tku, ale pozosta\u0142o jeszcze kilka pyta\u0144 dotycz\u0105cych b\u0142\u0119d\u00f3w \u2014 widocznych i niewidocznych.<\/p>\n<p><\/p>\n<p>Wr\u00f3\u0107my do pierwotnego zadania.<\/p>\n<p><\/p>\n<p>I program <em>sort<\/em> i program <em>join<\/em> korzystaj\u0105 z tych samych funkcji por\u00f3wnywania linii z <em>glibc<\/em>. Jak to si\u0119 mog\u0142o sta\u0107, \u017ce <em>join<\/em> generuje b\u0142\u0105d sortowania w linijkach posortowanych komend\u0105 <em>sort<\/em> w lokalizacji <em>en_US.UTF-8<\/em>? \u041e\u0442\u0432\u0435\u0442 \u043f\u0440\u043e\u0441\u0442: <em>sort<\/em> por\u00f3wnuje ca\u0142\u0105 lini\u0119, a <em>join<\/em> por\u00f3wnuje tylko klucz, kt\u00f3rym domy\u015blnie jest pocz\u0105tek linii a\u017c do pierwszego bia\u0142ego znaku. W moim przyk\u0142adzie prowadzi\u0142o to do komunikatu o b\u0142\u0119dzie, poniewa\u017c sortowanie pierwszych s\u0142\u00f3w w linijkach nie zgadza\u0142o si\u0119 z sortowaniem pe\u0142nych linii.<\/p>\n<p><\/p>\n<p>Lokalizacja <em>&quot;C&quot;<\/em> zapewnia, \u017ce w posortowanych linijkach pocz\u0105tki podci\u0105g\u00f3w a\u017c do pierwszej spacji r\u00f3wnie\u017c b\u0119d\u0105 posortowane, ale to tylko maskuje b\u0142\u0105d. Mo\u017cna znale\u017a\u0107 takie dane (ludzie o tych samych nazwiskach, ale r\u00f3\u017cnych imionach), kt\u00f3re bez komunikatu o b\u0142\u0119dzie dawa\u0142yby niew\u0142a\u015bciwy wynik przy scalaniu plik\u00f3w. Je\u015bli chcemy, aby <em>join<\/em> \u0142aczy\u0142 linie plik\u00f3w wed\u0142ug imienia i nazwiska, to poprawnym sposobem b\u0119dzie wyra\u017ane okre\u015blenie separatora p\u00f3l i sortowanie wed\u0142ug kluczowego pola, a nie wed\u0142ug ca\u0142ej linii. W tym przypadku i scalanie przebiegnie poprawnie i w \u017cadnej lokalizacji nie b\u0119dzie b\u0142\u0119d\u00f3w:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; sort -t ; -k 1 buhg.txt &gt; buhg.srt\n$&gt; sort -t ; -k 1 mail.txt &gt; mail.srt\n$&gt; join -t ; buhg.srt mail.srt &gt; result<\/code><\/pre>\n<p><\/p>\n<p>Przyk\u0142ad z sukcesem zako\u0144czony w kodowaniu <em>CP1251<\/em> zawiera jeszcze jeden b\u0142\u0105d. Chodzi o to, \u017ce we wszystkich znanych mi dystrybucjach <em>Linuxa<\/em> brakuje skompilowanej lokalizacji <em>ru_RU.CP1251<\/em>. Je\u015bli skompilowana lokalizacja nie zostanie znaleziona, to <em>sort<\/em> cicho u\u017cywa por\u00f3wnania bajtowego, co mogli\u015bmy obserwowa\u0107.<\/p>\n<p><\/p>\n<p>A propos, jest jeszcze jeden ma\u0142y bug zwi\u0105zany z brakiem dost\u0119pnych skompilowanych lokalizacji. Komenda <em>LOCPATH=\/tmp locale -a<\/em> wy\u015bwietli list\u0119 wszystkich lokalizacji w <em>locale-archive<\/em>, ale przy ustawionej zmiennej <em>LOCPATH<\/em> dla wszystkich program\u00f3w (w tym dla samej <em>locale<\/em>) te lokalizacje b\u0119d\u0105 niedost\u0119pne.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LOCPATH=\/tmp locale -a | grep en_US\nlocale: Nie mo\u017cna ustawi\u0107 LC_CTYPE na domy\u015bln\u0105 lokalizacj\u0119: Nie ma takiego pliku lub katalogu\nlocale: Nie mo\u017cna ustawi\u0107 LC_MESSAGES na domy\u015bln\u0105 lokalizacj\u0119: Nie ma takiego pliku lub katalogu\nlocale: Nie mo\u017cna ustawi\u0107 LC_COLLATE na domy\u015bln\u0105 lokalizacj\u0119: Nie ma takiego pliku lub katalogu\nen_US\nen_US.iso88591\nen_US.iso885915\nen_US.utf8\n\n$&gt; LC_COLLATE=en_US.UTF-8 sort --debug\nsort: u\u017cywam zasad sortowania \u2018en_US.UTF-8\u2019\n\n$&gt; LOCPATH=\/tmp LC_COLLATE=en_US.UTF-8 sort --debug\nsort: u\u017cywam prostego por\u00f3wnania bajt\u00f3w<\/code><\/pre>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Podsumowanie<\/h1>\n<p><\/p>\n<p>Je\u015bli jeste\u015b programist\u0105, kt\u00f3ry przywyk\u0142 uwa\u017ca\u0107, \u017ce linie to zbi\u00f3r bajt\u00f3w, to tw\u00f3j wyb\u00f3r <em>LC_COLLATE=C<\/em>.<\/p>\n<p><\/p>\n<p>Je\u015bli jeste\u015b lingwist\u0105 lub redaktorem s\u0142ownik\u00f3w, lepiej, aby\u015b skompilowa\u0142 swoj\u0105 lokalizacj\u0119.<\/p>\n<p><\/p>\n<p>Je\u015bli jeste\u015b zwyk\u0142ym u\u017cytkownikiem, wystarczy, \u017ce przyzwyczaisz si\u0119 do tego, \u017ce komenda <em>ls -a<\/em> wy\u015bwietla pliki zaczynaj\u0105ce si\u0119 od kropki, wymieszane z plikami zaczynaj\u0105cymi si\u0119 od litery, a <em>Midnight Commander<\/em>, kt\u00f3ry u\u017cywa swoich wewn\u0119trznych funkcji do sortowania imion, umieszcza pliki zaczynaj\u0105ce si\u0119 od kropki na pocz\u0105tku listy.<\/p>\n<p><\/p>\n<h1 id=\"ssylki\">Linki<\/h1>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/unicode.org\/reports\/tr10\/\">Raport nr 10 algorytmu sortowania Unicode <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/www.unicode.org\/Public\/UCA\/latest\/allkeys.txt\">Wagi znak\u00f3w na unicode.org <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/userguide.icu-project.org\/intro\"><em>ICU<\/em> \u2014 implementacja biblioteki do pracy z Unicode od IBM. <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/demo.icu-project.org\/icu-bin\/collation.html\">Test sortowania przy u\u017cyciu <em>ICU<\/em> <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/www.iso.org\/ittf\/ISO14651_2006_TABLE1_en.txt\">Wagi znak\u00f3w w <em>ISO 14651<\/em> <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/www.open-std.org\/JTC1\/SC22\/WG20\/docs\/n972-14652ft.pdf\">Opis formatu pliku z wagami <em>ISO 14652<\/em> <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/sourceware.org\/bugzilla\/show_bug.cgi?id=14095\">Dyskusja na temat por\u00f3wnywania ci\u0105g\u00f3w w <em>glibc<\/em><\/a><\/noindex><\/p>\n<p>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/503960\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0412\u0441\u0451 \u043d\u0430\u0447\u0430\u043b\u043e\u0441\u044c \u0441 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u043b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0430\u0434\u0440\u0435\u0441\u0430\u0445 e-mail \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0441 \u0434\u043e\u043b\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u0438\u0437 \u0431\u0430\u0437\u044b \u043e\u0442\u0434\u0435\u043b\u0430 \u043a\u0430\u0434\u0440\u043e\u0432. \u041e\u0431\u0430 \u0441\u043f\u0438\u0441\u043a\u0430 \u0431\u044b\u043b\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0435 \u042e\u043d\u0438\u043a\u043e\u0434 UTF-8 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0441 \u044e\u043d\u0438\u043a\u0441\u043e\u0432\u0441\u043a\u0438\u043c\u0438 \u043a\u043e\u043d\u0446\u0430\u043c\u0438 \u0441\u0442\u0440\u043e\u043a. \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 mail.txt \u0418\u0432\u0430\u043d\u043e\u0432 \u0410\u043d\u0434\u0440\u0435\u0439;ia@example.com \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 buhg.txt \u0418\u0432\u0430\u043d\u043e\u0432\u0430 \u0410\u043b\u043b\u0430;\u043c\u0430\u043b\u044f\u0440 \u0401\u043b\u043a\u0438\u043d\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-83055","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0412\u0441\u0451 \u043d\u0430\u0447\u0430\u043b\u043e\u0441\u044c \u0441 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u043b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0430\u0434\u0440\u0435\u0441\u0430\u0445 e-mail \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0441.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041a\u0430\u043a Linux\u2019\u043e\u0432\u0441\u043a\u0438\u0439 sort \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0438 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0412\u0441\u0451 \u043d\u0430\u0447\u0430\u043b\u043e\u0441\u044c \u0441 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u043b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0430\u0434\u0440\u0435\u0441\u0430\u0445 e-mail \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0441.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-05-27T23:42:15+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-27T23:42:15+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Jak sortowanie w Linuxie sortuje ci\u0105gi | ProHoster","description":"Wprowadzenie Wszystko zacz\u0119\u0142o si\u0119 od kr\u00f3tkiego skryptu, kt\u00f3ry mia\u0142 po\u0142\u0105czy\u0107 informacje o adresach e-mail pracownik\u00f3w uzyskane z listy u\u017cytkownik\u00f3w newslettera z.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041a\u0430\u043a Linux\u2019\u043e\u0432\u0441\u043a\u0438\u0439 sort \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0438 | ProHoster","og:description":"\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0412\u0441\u0451 \u043d\u0430\u0447\u0430\u043b\u043e\u0441\u044c \u0441 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u043b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0430\u0434\u0440\u0435\u0441\u0430\u0445 e-mail \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0441.","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-05-27T23:42:15+00:00","article:modified_time":"2020-05-27T23:42:15+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"83055","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 15:26:22","updated":"2022-09-27 19:16:08","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/83055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=83055"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/83055\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=83055"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=83055"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=83055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}