{"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\/pt\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki","title":{"rendered":"Como o sort do Linux ordena strings","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h1 id=\"vvedenie\">Introdu\u00e7\u00e3o<\/h1>\n<p><\/p>\n<p>Tudo come\u00e7ou com um pequeno script que deveria combinar informa\u00e7\u00f5es sobre endere\u00e7os <em>e-mail<\/em> de funcion\u00e1rios, obtidas de uma lista de usu\u00e1rios de mailing, com as posi\u00e7\u00f5es dos funcion\u00e1rios, obtidas do banco de dados do departamento de pessoal. Ambas as listas foram exportadas para arquivos de texto em codifica\u00e7\u00e3o Unicode <em>UTF-8<\/em> e salvas com quebras de linha no formato Unix.<\/p>\n<p><\/p>\n<p>Conte\u00fado <em>mail.txt<\/em><\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Ivanov Andrei;ia@example.com<\/code><\/pre>\n<p><\/p>\n<p>Conte\u00fado <em>buhg.txt<\/em><\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Ivanova Alla;pintora\nYelkina Ella;guindasteira\nIvanov Andrei;serralheiro\nAbakanov Mikhail;pintor<\/code><\/pre>\n<p><\/p>\n<p>Para combinar, os arquivos foram ordenados usando um comando Unix <em>sort<\/em> e fornecidos como entrada para um programa Unix <em>join<\/em>, que inesperadamente terminou com um erro: <\/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: n\u00e3o est\u00e1 ordenado: Ivanov Andrei;serralheiro<\/code><\/pre>\n<p><\/p>\n<p>A visualiza\u00e7\u00e3o do resultado da ordena\u00e7\u00e3o mostrou que, de modo geral, a ordena\u00e7\u00e3o est\u00e1 correta, mas em casos de coincid\u00eancias de sobrenomes masculinos e femininos, os femininos aparecem antes dos masculinos:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; sort buhg.txt\nAbakanov Mikhail;pintor\nYelkina Ella;guindasteira\nIvanova Alla;pintora\nIvanov Andrei;serralheiro<\/code><\/pre>\n<p><\/p>\n<p>Parece ser um bug na ordena\u00e7\u00e3o em Unicode ou uma manifesta\u00e7\u00e3o de feminismo no algoritmo de ordena\u00e7\u00e3o. A primeira, claro, \u00e9 mais plaus\u00edvel.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Vamos deixar isso de lado <em>join<\/em> e nos concentrar em <em>sort<\/em>. Vamos tentar resolver o problema pelo m\u00e9todo da tentativa e erro. Para come\u00e7ar, vamos mudar a localidade de <em>en_US<\/em> para <em>ru_RU<\/em>. Para a ordena\u00e7\u00e3o, teria sido suficiente definir a vari\u00e1vel de ambiente <em>LC_COLLATE<\/em>, mas n\u00e3o vamos ser mesquinhos:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=ru_RU.UTF-8 sort buhg.txt\nAbakanov Mikhail;pintor\nYelkina Ella;guindasteira\nIvanova Alla;pintora\nIvanov Andrei;serralheiro<\/code><\/pre>\n<p><\/p>\n<p>Nada mudou.<\/p>\n<p><\/p>\n<p>Vamos tentar reencodar os arquivos em uma codifica\u00e7\u00e3o de byte \u00fanico: <\/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>Novamente, nada mudou.<\/p>\n<p><\/p>\n<p>N\u00e3o tem jeito, teremos que buscar a solu\u00e7\u00e3o na internet. N\u00e3o h\u00e1 nada espec\u00edfico sobre sobrenomes russos, mas existem perguntas sobre outras estranhezas da ordena\u00e7\u00e3o. Veja, por exemplo, este problema: <noindex><a rel=\"nofollow\" href=\"https:\/\/serverfault.com\/questions\/95579\/unix-sort-treats-dash-characters-as-invisible\/95593\">O sort do UNIX trata os caracteres &#8216;-&#8216; (h\u00edfen) como invis\u00edveis<\/a><\/noindex>. Resumindo, as strings &quot;a-b&quot;, &quot;aa&quot;, &quot;ac&quot; s\u00e3o classificadas como &quot;aa&quot;, &quot;a-b&quot;, &quot;ac&quot;.<\/p>\n<p><\/p>\n<p>A resposta em todos os lugares \u00e9 padr\u00e3o: use a localidade de programa\u00e7\u00e3o <em>&quot;C&quot;<\/em> e ser\u00e1 a sua felicidade. Vamos tentar:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=C sort buhg.txt\nYelkina Ella;guindasteira\nAbakanov Mikhail;pintor\nIvanov Andrei;serralheiro\nIvanova Alla;advogada<\/code><\/pre>\n<p><\/p>\n<p>Algo mudou. Os Ivanovs foram organizados na ordem correta, embora Yelkina tenha desaparecido. Vamos voltar \u00e0 tarefa original:<\/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>Funcionou sem erros, como prometido pela internet. E isso apesar de \u0401\u043b\u043a\u0438\u043d na primeira linha.<\/p>\n<p><\/p>\n<p>O problema parece ter sido resolvido, mas por precau\u00e7\u00e3o vamos tentar mais uma codifica\u00e7\u00e3o russa \u2014 a do Windows. <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>O resultado da ordena\u00e7\u00e3o, curiosamente, coincidir\u00e1 com a localidade <em>&quot;C&quot;<\/em>, e todo o exemplo, consequentemente, passa sem erros. \u00c9 uma esp\u00e9cie de misticismo.<\/p>\n<p><\/p>\n<p>Eu n\u00e3o gosto de misticismo na programa\u00e7\u00e3o, pois, geralmente, isso esconde erros. Terei que me debru\u00e7ar seriamente sobre a quest\u00e3o de como funciona <em>sort<\/em> e o que influencia <em>LC_COLLATE<\/em> .<\/p>\n<p><\/p>\n<p>No final, tentarei responder \u00e0s perguntas:<\/p>\n<p><\/p>\n<ul>\n<li>por que os sobrenomes femininos estavam sendo ordenados incorretamente<\/li>\n<li>por que <em>LANG=ru_RU.CP1251<\/em> acabou sendo equivalente <em>LANG=C<\/em><\/li>\n<li>por que h\u00e1 <em>sort<\/em> e <em>join<\/em> diferentes representa\u00e7\u00f5es da ordem das linhas ordenadas<\/li>\n<li>por que h\u00e1 erros em todos os meus exemplos<\/li>\n<li>e, finalmente, como ordenar linhas ao meu gosto<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"sortirovka-v-yunikode\">Ordena\u00e7\u00e3o em Unicode<\/h1>\n<p><\/p>\n<p>A primeira parada ser\u00e1 o relat\u00f3rio t\u00e9cnico n\u00ba 10 intitulado <noindex><a rel=\"nofollow\" href=\"https:\/\/unicode.org\/reports\/tr10\/\">Unicode collation algorithm<\/a><\/noindex> no site <noindex><a rel=\"nofollow\" href=\"https:\/\/unicode.org\">unicode.org<\/a><\/noindex>. O relat\u00f3rio cont\u00e9m muitos detalhes t\u00e9cnicos, ent\u00e3o permitirei-me apresentar um resumo das principais ideias.<\/p>\n<p><\/p>\n<p><em>Cola\u00e7\u00e3o<\/em> \u2014 &quot;compara\u00e7\u00e3o&quot; de strings \u2014 \u00e9 a base de qualquer algoritmo de ordena\u00e7\u00e3o. Os pr\u00f3prios algoritmos podem variar (&quot;bolha&quot;, &quot;mesclagem&quot;, &quot;r\u00e1pido&quot;), mas todos usar\u00e3o a compara\u00e7\u00e3o de pares de strings para determinar a ordem em que devem aparecer.<\/p>\n<p><\/p>\n<p>A ordena\u00e7\u00e3o de strings em uma l\u00edngua natural \u00e9 um problema bastante complexo. Mesmo nas mais simples codifica\u00e7\u00f5es de um byte, a ordem das letras no alfabeto, mesmo que algo diferente do alfabeto latino ingl\u00eas, j\u00e1 n\u00e3o coincidir\u00e1 com a ordem dos valores num\u00e9ricos com os quais essas letras s\u00e3o codificadas. Assim, na l\u00edngua alem\u00e3, a letra <em>\u00d6<\/em> ocupa uma posi\u00e7\u00e3o entre <em>A<\/em> e <em>P<\/em>, enquanto na codifica\u00e7\u00e3o <em>CP850<\/em> ela entre <em>\u00ff<\/em> e <em>\u00dc<\/em>.<\/p>\n<p><\/p>\n<p>Podemos tentar nos abstrair da codifica\u00e7\u00e3o espec\u00edfica e considerar letras &quot;ideais&quot;, que est\u00e3o organizadas em uma certa ordem, como feito no Unicode. As codifica\u00e7\u00f5es <em>UTF8<\/em>, <em>UTF16<\/em> ou a de um byte <em>KOI8-R<\/em> (se for necess\u00e1rio um subconjunto limitado do Unicode) dar\u00e3o diferentes representa\u00e7\u00f5es num\u00e9ricas das letras, mas referindo-se aos mesmos elementos da tabela b\u00e1sica. <\/p>\n<p><\/p>\n<p>Descobriu-se que mesmo construindo uma tabela de caracteres do zero, n\u00e3o conseguiremos estabelecer uma ordem universal para os caracteres. Em diferentes alfabetos nacionais que utilizam as mesmas letras, a ordem dessas letras pode variar. Por exemplo, na l\u00edngua francesa <em>\u00c6<\/em> ser\u00e1 considerada uma ligadura e ordenada como uma string <em>AE<\/em>. J\u00e1 na l\u00edngua norueguesa <em>\u00c6<\/em> ser\u00e1 uma letra separada, que aparece ap\u00f3s <em>Z<\/em>. A prop\u00f3sito, al\u00e9m de ligaduras como <em>\u00c6<\/em> existem letras que s\u00e3o escritas com v\u00e1rios s\u00edmbolos. Assim, no alfabeto tcheco, h\u00e1 a letra <em>Ch<\/em>, que est\u00e1 entre <em>H<\/em> e <em>I<\/em>.<\/p>\n<p><\/p>\n<p>Al\u00e9m das diferen\u00e7as nos alfabetos, existem outras tradi\u00e7\u00f5es nacionais que influenciam a ordena\u00e7\u00e3o. Em particular, surge a quest\u00e3o: qual deve ser a ordem das palavras em um dicion\u00e1rio que consistem de letras mai\u00fasculas e min\u00fasculas? Al\u00e9m disso, a pontua\u00e7\u00e3o pode influenciar a ordena\u00e7\u00e3o. Na l\u00edngua espanhola, no in\u00edcio de uma frase interrogativa, coloca-se um ponto de interroga\u00e7\u00e3o invertido (<em>\u00bfTe gusta la m\u00fasica?<\/em>). Nesse caso, \u00e9 evidente que as frases interrogativas n\u00e3o devem ser agrupadas em um cluster separado fora do alfabeto, mas como devem ser ordenadas as strings com outros sinais de pontua\u00e7\u00e3o?<\/p>\n<p><\/p>\n<p>N\u00e3o vou entrar na ordena\u00e7\u00e3o de strings em idiomas que diferem muito dos europeus. Destaco que em l\u00ednguas com dire\u00e7\u00e3o de escrita da direita para a esquerda ou de cima para baixo, os caracteres nas strings provavelmente s\u00e3o armazenados na ordem de leitura, e mesmo em sistemas de escrita n\u00e3o alfab\u00e9ticos existem maneiras pr\u00f3prias de ordenar strings caracter por caracter. Por exemplo, os ideogramas podem ser ordenados de acordo com os seus tra\u00e7os (<noindex><a rel=\"nofollow\" href=\"https:\/\/studychinese.ru\/kljuchi\/\">chaves de ideogramas chineses<\/a><\/noindex>) ou pela pron\u00fancia. Como os emojis devem ser ordenados, honestamente n\u00e3o sei, mas tamb\u00e9m pode-se inventar algo para eles.<\/p>\n<p><\/p>\n<p>Com base nas caracter\u00edsticas mencionadas acima, foram formulados os principais requisitos para a compara\u00e7\u00e3o de strings, baseados em tabelas Unicode:<\/p>\n<p><\/p>\n<ul>\n<li>a compara\u00e7\u00e3o de strings n\u00e3o depende da posi\u00e7\u00e3o dos caracteres na tabela de c\u00f3digos;<\/li>\n<li>as sequ\u00eancias de caracteres que formam um \u00fanico s\u00edmbolo s\u00e3o trazidas \u00e0 forma can\u00f4nica (<em>A<\/em> + um c\u00edrculo superior \u00e9 o mesmo que <em>\u00c5<\/em>);<\/li>\n<li>) ao comparar strings, o s\u00edmbolo \u00e9 considerado no contexto da string e, quando necess\u00e1rio, \u00e9 combinado com vizinhos em uma \u00fanica unidade de compara\u00e7\u00e3o (<em>Ch<\/em> em tcheco) ou dividido em v\u00e1rias (<em>\u00c6<\/em> em franc\u00eas);<\/li>\n<li>todas as caracter\u00edsticas nacionais (alfabeto, mai\u00fasculas\/min\u00fasculas, pontua\u00e7\u00e3o, ordem dos tipos de escrita) devem ser configuradas at\u00e9 a atribui\u00e7\u00e3o manual da ordem (emoji);<\/li>\n<li>a compara\u00e7\u00e3o \u00e9 importante n\u00e3o apenas para a ordena\u00e7\u00e3o, mas tamb\u00e9m em muitos outros lugares, como para definir intervalos de linhas (substitui\u00e7\u00e3o {A\u2026 z} em <em>bash<\/em>);<\/li>\n<li>a compara\u00e7\u00e3o deve ser realizada de forma suficientemente r\u00e1pida.<\/li>\n<\/ul>\n<p><\/p>\n<p>Al\u00e9m disso, os autores do relat\u00f3rio formularam propriedades de compara\u00e7\u00e3o nas quais os desenvolvedores de algoritmos n\u00e3o devem confiar:<\/p>\n<p><\/p>\n<ul>\n<li>o algoritmo de compara\u00e7\u00e3o n\u00e3o deve exigir um conjunto separado de caracteres para cada idioma (o russo e o ucraniano usam a maioria dos caracteres cir\u00edlicos em conjunto);<\/li>\n<li>a compara\u00e7\u00e3o n\u00e3o deve depender da ordem dos caracteres nas tabelas Unicode;<\/li>\n<li>o peso da string n\u00e3o deve ser um atributo da string, uma vez que a mesma string em diferentes contextos culturais pode ter pesos diferentes;<\/li>\n<li>os pesos das strings podem mudar ao mesclar ou dividir (de <em>x<\/em> &lt; <em>y<\/em> n\u00e3o se deve entender que <em>xz<\/em> &lt; <em>yz<\/em>);<\/li>\n<li>strings diferentes que t\u00eam o mesmo peso s\u00e3o consideradas iguais do ponto de vista do algoritmo de ordena\u00e7\u00e3o. A introdu\u00e7\u00e3o de uma ordena\u00e7\u00e3o adicional dessas strings \u00e9 poss\u00edvel, mas pode prejudicar o desempenho;<\/li>\n<li>em ordena\u00e7\u00f5es subsequentes, strings que t\u00eam o mesmo peso podem trocar de lugar. A estabilidade \u00e9 uma propriedade de um algoritmo espec\u00edfico de ordena\u00e7\u00e3o, e n\u00e3o uma propriedade do algoritmo de compara\u00e7\u00e3o de strings (veja o ponto anterior);<\/li>\n<li>as regras de ordena\u00e7\u00e3o podem mudar ao longo do tempo \u00e0 medida que as tradi\u00e7\u00f5es culturais s\u00e3o refinadas\/mudadas.<\/li>\n<\/ul>\n<p><\/p>\n<p>Assim, est\u00e1 estipulado que o algoritmo de compara\u00e7\u00e3o n\u00e3o sabe nada sobre a sem\u00e2ntica das strings processadas. Portanto, strings compostas apenas por d\u00edgitos n\u00e3o devem ser comparadas como n\u00fameros, e nos listas de nomes em ingl\u00eas, o artigo n\u00e3o deve ser removido (<em>Beatles, The<\/em>).<\/p>\n<p><\/p>\n<p>Para atender a todos os requisitos mencionados, foi proposto um algoritmo de ordena\u00e7\u00e3o em m\u00faltiplos n\u00edveis (na verdade, de quatro n\u00edveis).<\/p>\n<p><\/p>\n<p>Preliminarmente, os caracteres da string s\u00e3o convertidos para a forma can\u00f4nica e agrupados em unidades de compara\u00e7\u00e3o. A cada unidade de compara\u00e7\u00e3o s\u00e3o atribu\u00eddos v\u00e1rios pesos, correspondendo a diferentes n\u00edveis de compara\u00e7\u00e3o. Os pesos das unidades de compara\u00e7\u00e3o s\u00e3o elementos de conjuntos ordenados (neste caso, n\u00fameros inteiros) que podem ser comparados em rela\u00e7\u00e3o a maior-menor. Um valor especial <em>IGNORED<\/em> (0x0) significa que, no n\u00edvel de compara\u00e7\u00e3o correspondente, essa unidade n\u00e3o participa da compara\u00e7\u00e3o. A compara\u00e7\u00e3o de strings pode ser repetida v\u00e1rias vezes, usando os pesos dos n\u00edveis correspondentes. Em cada um dos n\u00edveis, os pesos das unidades de compara\u00e7\u00e3o de duas strings s\u00e3o comparados sequencialmente entre si.<\/p>\n<p><\/p>\n<p>Em v\u00e1rias implementa\u00e7\u00f5es do algoritmo para diferentes tradi\u00e7\u00f5es nacionais, os valores dos coeficientes podem variar, mas a tabela b\u00e1sica de pesos inclui-se no padr\u00e3o Unicode \u2014 <em>&quot;Tabela de Elementos de Cola\u00e7\u00e3o Padr\u00e3o do Unicode&quot;<\/em> (<em>DUCET<\/em>). Desejo observar que a defini\u00e7\u00e3o da vari\u00e1vel <em>LC_COLLATE<\/em> \u00e9 na verdade a indica\u00e7\u00e3o da escolha da tabela de pesos na fun\u00e7\u00e3o de compara\u00e7\u00e3o de strings.<\/p>\n<p><\/p>\n<p>Os coeficientes de peso <em>DUCET<\/em> s\u00e3o dispostos da seguinte maneira:<\/p>\n<p><\/p>\n<ul>\n<li>no primeiro n\u00edvel, todas as letras s\u00e3o convertidas para um \u00fanico caso, os diacr\u00edticos s\u00e3o descartados e a pontua\u00e7\u00e3o (n\u00e3o toda) \u00e9 ignorada;<\/li>\n<li>no segundo n\u00edvel, apenas os diacr\u00edticos s\u00e3o levados em conta;<\/li>\n<li>no terceiro n\u00edvel, apenas o caso \u00e9 considerado;<\/li>\n<li>no quarto n\u00edvel, apenas a pontua\u00e7\u00e3o \u00e9 considerada.<\/li>\n<\/ul>\n<p><\/p>\n<p>A compara\u00e7\u00e3o ocorre em v\u00e1rias passagens: primeiro, os coeficientes do primeiro n\u00edvel s\u00e3o comparados; se os pesos coincidirem, uma nova compara\u00e7\u00e3o com os pesos do segundo n\u00edvel ocorre; ent\u00e3o, talvez, do terceiro e quarto n\u00edveis.<\/p>\n<p><\/p>\n<p>A compara\u00e7\u00e3o termina quando as strings cont\u00eam unidades de compara\u00e7\u00e3o correspondentes com pesos diferentes. Strings que t\u00eam pesos iguais em todos os quatro n\u00edveis s\u00e3o consideradas iguais entre si.<\/p>\n<p><\/p>\n<p>Este algoritmo (com muitas detalhes t\u00e9cnicos adicionais) deu nome ao relat\u00f3rio n\u00ba 10 \u2014 <em>&quot;Algoritmo de Cola\u00e7\u00e3o do Unicode&quot;<\/em> (<em>UCA<\/em>).<\/p>\n<p><\/p>\n<p>Neste ponto, o comportamento de classifica\u00e7\u00e3o do nosso exemplo torna-se um pouco mais claro. Seria bom compar\u00e1-lo com o padr\u00e3o Unicode.<\/p>\n<p><\/p>\n<p>Para testar implementa\u00e7\u00f5es <em>UCA<\/em> existe um <noindex><a rel=\"nofollow\" href=\"https:\/\/www.unicode.org\/Public\/UCA\/latest\/CollationTest.html\">teste<\/a><\/noindex>, utilizando <noindex><a rel=\"nofollow\" href=\"http:\/\/www.unicode.org\/Public\/UCA\/latest\/allkeys.txt\">um arquivo de pesos<\/a><\/noindex>, implementando <em>DUCET<\/em>. No arquivo de pesos, voc\u00ea pode encontrar v\u00e1rias curiosidades. Por exemplo, h\u00e1 a ordem das pe\u00e7as de mahjong e do domin\u00f3 europeu, assim como a ordem dos naipes em um baralho de cartas (s\u00edmbolo <em>1F000<\/em> e assim por diante). Os naipes est\u00e3o dispostos de acordo com as regras do bridge \u2014 PCHBT, e as cartas nos naipes \u2014 na sequ\u00eancia T,2,3\u2026 K.<\/p>\n<p><\/p>\n<p>Verifica\u00e7\u00e3o manual da corre\u00e7\u00e3o da classifica\u00e7\u00e3o das strings de acordo com <em>DUCET<\/em> seria bastante cansativa, mas, felizmente para n\u00f3s, existe uma implementa\u00e7\u00e3o exemplar de uma biblioteca para trabalhar com 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>No site dessa biblioteca, desenvolvida em <em>IBM<\/em>, h\u00e1 p\u00e1ginas de demonstra\u00e7\u00e3o, incluindo <noindex><a rel=\"nofollow\" href=\"http:\/\/demo.icu-project.org\/icu-bin\/collation.html\">a p\u00e1gina do algoritmo de compara\u00e7\u00e3o de strings<\/a><\/noindex>. Inserimos nossas strings de teste com as configura\u00e7\u00f5es padr\u00e3o e, oh maravilha, obtemos uma perfeita ordena\u00e7\u00e3o russa.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Abakanov Mikhail;pintor\nYolkina Ella;operadora de guindaste\nIvanov Andrei;serralheiro\nIvanova Alla;advogada<\/code><\/pre>\n<p><\/p>\n<p>A prop\u00f3sito, no site <em>ICU<\/em> \u00e9 poss\u00edvel encontrar esclarecimentos sobre o funcionamento do algoritmo de compara\u00e7\u00e3o ao lidar com sinais de pontua\u00e7\u00e3o. Nos exemplos <noindex><a rel=\"nofollow\" href=\"http:\/\/userguide.icu-project.org\/collation\/faq\">Collation FAQ<\/a><\/noindex> s\u00e3o ignorados ap\u00f3strofo e h\u00edfen.<\/p>\n<p><\/p>\n<p>O Unicode nos ajudou, mas procurar as raz\u00f5es para o comportamento estranho <em>sort<\/em> em <em>Linux<\/em> teremos que faz\u00ea-lo em outro lugar.<\/p>\n<p><\/p>\n<h1 id=\"sortirovka-v-glibc\">Ordena\u00e7\u00e3o em glibc<\/h1>\n<p><\/p>\n<p>Uma visualiza\u00e7\u00e3o r\u00e1pida do c\u00f3digo fonte da ferramenta <em>sort<\/em> do <em>GNU Core Utils<\/em> mostrou que na pr\u00f3pria ferramenta a localiza\u00e7\u00e3o se resume a imprimir o valor atual da vari\u00e1vel <em>LC_COLLATE<\/em> ao ser executada no modo de depura\u00e7\u00e3o:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$ sort --debug buhg.txt &gt; buhg.srt\nsort: usando regras de ordena\u00e7\u00e3o \u2018en_US.UTF8\u2019<\/code><\/pre>\n<p><\/p>\n<p>A compara\u00e7\u00e3o de strings \u00e9 realizada pela fun\u00e7\u00e3o padr\u00e3o <em>strcoll<\/em>, ou seja, tudo que nos interessa est\u00e1 na biblioteca <em>glibc<\/em>.<\/p>\n<p><\/p>\n<p>Sobre <em>wiki<\/em> Apache Traffic Control <em>glibc<\/em> a compara\u00e7\u00e3o de strings \u00e9 tratada em <noindex><a rel=\"nofollow\" href=\"https:\/\/sourceware.org\/glibc\/wiki\/Locales#LC_COLLATE\">um par\u00e1grafo<\/a><\/noindex>. Desse par\u00e1grafo podemos entender que em <em>glibc<\/em> a ordena\u00e7\u00e3o \u00e9 baseada no algoritmo que j\u00e1 conhecemos <em>UCA<\/em> (<em>The Unicode collation algorithm<\/em>) e\/ou em um padr\u00e3o semelhante <em>ISO 14651<\/em> (<em>International string ordering and comparison<\/em>). Sobre o \u00faltimo padr\u00e3o, vale mencionar que no site <noindex><a rel=\"nofollow\" href=\"https:\/\/standards.iso.org\/ittf\/PubliclyAvailableStandards\">standards.iso.org<\/a><\/noindex> <em>ISO 14651<\/em> \u00e9 oficialmente declarado de acesso p\u00fablico, mas o link correspondente leva a uma p\u00e1gina inexistente. O Google retorna v\u00e1rias p\u00e1ginas com links para sites oficiais que oferecem a c\u00f3pia eletr\u00f4nica do padr\u00e3o por uma centena de euros, mas na terceira ou quarta p\u00e1gina dos resultados de busca podem ser encontrados links diretos para <em>PDF<\/em>. No geral, o padr\u00e3o n\u00e3o difere muito do <em>UCA<\/em>, mas \u00e9 mais ma\u00e7ante de ler, pois n\u00e3o cont\u00e9m exemplos marcantes das particularidades nacionais na ordena\u00e7\u00e3o de strings. <\/p>\n<p><\/p>\n<p>A informa\u00e7\u00e3o mais interessante em <em>wiki<\/em> foi o link para <noindex><a rel=\"nofollow\" href=\"https:\/\/sourceware.org\/bugzilla\/show_bug.cgi?id=14095\">o rastreador de bugs<\/a><\/noindex> com a discuss\u00e3o sobre a implementa\u00e7\u00e3o da compara\u00e7\u00e3o de strings em <em>glibc<\/em>. Da discuss\u00e3o, podemos descobrir que em <em>glibc<\/em> para a compara\u00e7\u00e3o de strings \u00e9 utilizada <em>ISO<\/em>a tabela <noindex><a rel=\"nofollow\" href=\"http:\/\/www.iso.org\/ittf\/ISO14651_2006_TABLE1_en.txt\">The Common Template Table<\/a><\/noindex> (<em>CTT<\/em>), cujo endere\u00e7o pode ser encontrado no ap\u00eandice <em>A<\/em> do padr\u00e3o <em>ISO 14651<\/em>. Entre 2000 e 2015, essa tabela em <em>glibc<\/em> n\u00e3o tinha um mantenedor e diferia bastante (pelo menos externamente) da vers\u00e3o atual do padr\u00e3o. De 2015 a 2018, houve uma adapta\u00e7\u00e3o \u00e0 nova vers\u00e3o da tabela e, atualmente, voc\u00ea pode encontrar na pr\u00e1tica tanto a nova vers\u00e3o da tabela (<em>CentOS 8<\/em>) quanto a antiga (<em>CentOS 7<\/em>). <\/p>\n<p><\/p>\n<p>Agora que temos todas as informa\u00e7\u00f5es sobre o algoritmo e as tabelas auxiliares, podemos voltar ao problema original e entender como classificar corretamente as linhas na localidade russa.<\/p>\n<p><\/p>\n<h1 id=\"iso-1465114652\">ISO 14651\/14652<\/h1>\n<p><\/p>\n<p>O c\u00f3digo-fonte da tabela que nos interessa <em>CTT<\/em> na maioria das distribui\u00e7\u00f5es <em>Linux<\/em> est\u00e1 localizado no diret\u00f3rio <em>\/usr\/share\/i18n\/locales\/<\/em>. A pr\u00f3pria tabela est\u00e1 no arquivo <em>iso14651_t1_common<\/em>. Depois, esse arquivo \u00e9 inclu\u00eddo no arquivo de diretiva <em>copy iso14651_t1_common<\/em> que \u00e9 inclu\u00eddo no arquivo <em>iso14651_t1<\/em>, que, por sua vez, \u00e9 inclu\u00eddo nos arquivos nacionais, incluindo <em>en_US<\/em> e <em>ru_RU<\/em>. Na maioria das distribui\u00e7\u00f5es <em>Linux<\/em> todos os arquivos-fonte est\u00e3o inclu\u00eddos na instala\u00e7\u00e3o b\u00e1sica, mas se eles n\u00e3o estiverem presentes, ser\u00e1 necess\u00e1rio instalar o pacote adicional da distribui\u00e7\u00e3o.<\/p>\n<p><\/p>\n<p>A estrutura do arquivo <em>iso14651_t1<\/em> pode parecer horrivelmente prolixo, com regras de constru\u00e7\u00e3o de nomes pouco claras, mas, se voc\u00ea entender, tudo \u00e9 bem simples. A estrutura \u00e9 descrita no padr\u00e3o <em>ISO 14652<\/em>, uma c\u00f3pia do qual pode ser baixada do site <noindex><a rel=\"nofollow\" href=\"http:\/\/www.open-std.org\/JTC1\/SC22\/WG20\/docs\/n972-14652ft.pdf\">open-std.org<\/a><\/noindex>. Outra descri\u00e7\u00e3o do formato do arquivo pode ser lida no <noindex><a rel=\"nofollow\" href=\"https:\/\/pubs.opengroup.org\/onlinepubs\/9699919799\/basedefs\/V1_chap07.html\">especifica\u00e7\u00f5es<\/a><\/noindex> <em>POSIX<\/em> de <em>OpenGroup<\/em>. Como alternativa \u00e0 leitura do padr\u00e3o, voc\u00ea pode estudar os c\u00f3digos-fonte da fun\u00e7\u00e3o <em>collate_read<\/em> em <em>glibc\/locale\/programs\/ld-collate.c<\/em>.<\/p>\n<p><\/p>\n<p>A estrutura do arquivo \u00e9 a seguinte:<\/p>\n<p><\/p>\n<p>Por padr\u00e3o, o s\u00edmbolo \u00e9 usado como o caractere de escape e o final da linha ap\u00f3s o s\u00edmbolo # \u00e9 um coment\u00e1rio. Ambos os s\u00edmbolos podem ser redefinidos, o que foi feito na nova vers\u00e3o da tabela:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">escape_char \/\ncomment_char %<\/code><\/pre>\n<p><\/p>\n<p>O arquivo conter\u00e1 tokens no formato <em>&lt;Uxxxx&gt;<\/em> ou <em>&lt;Uxxxxxxxx&gt;<\/em> (onde <em>x<\/em> \u2014 d\u00edgito hexadecimal). Esta \u00e9 a representa\u00e7\u00e3o hexadecimal dos pontos de c\u00f3digo Unicode na codifica\u00e7\u00e3o <em>UCS-4<\/em> (<em>UTF-32<\/em>). Todos os outros elementos entre colchetes (incluindo <em>&lt;Uxxxx_xxxx&gt;<\/em>, <em>&lt;2&gt;<\/em> e semelhantes), s\u00e3o consideradas constantes de string simples, sem significado especial fora do contexto.<\/p>\n<p><\/p>\n<p>String <em>LC_COLLATE<\/em> diz-nos que os dados que descrevem a compara\u00e7\u00e3o de strings come\u00e7am a seguir.<\/p>\n<p><\/p>\n<p>Primeiro, s\u00e3o definidos os nomes para os pesos na tabela de compara\u00e7\u00e3o e os nomes para as combina\u00e7\u00f5es de caracteres. De maneira geral, dois tipos de nomes pertencem a duas entidades diferentes, mas no arquivo real eles est\u00e3o misturados. Os nomes dos pesos s\u00e3o definidos pela palavra-chave <em>collating-symbol<\/em> (caractere de compara\u00e7\u00e3o), pois, ao comparar caracteres Unicode com pesos id\u00eanticos, eles ser\u00e3o considerados como caracteres equivalentes.<\/p>\n<p><\/p>\n<p>O comprimento total da se\u00e7\u00e3o na revis\u00e3o atual do arquivo \u00e9 de cerca de 900 linhas. Eu extra\u00ed exemplos de v\u00e1rios lugares para mostrar a arbitrariedade dos nomes e alguns tipos de sintaxe.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">LC_COLLATE\n\ns\u00edmbolo de cola\u00e7\u00e3o &lt;RES-1&gt;\ns\u00edmbolo de cola\u00e7\u00e3o &lt;BLK&gt;\ns\u00edmbolo de cola\u00e7\u00e3o &lt;MIN&gt;\ns\u00edmbolo de cola\u00e7\u00e3o &lt;WIDE&gt;\n...\ns\u00edmbolo de cola\u00e7\u00e3o &lt;ARABIC&gt;\ns\u00edmbolo de cola\u00e7\u00e3o &lt;ETHPC&gt;\ns\u00edmbolo de cola\u00e7\u00e3o &lt;OSMANYA&gt;\n...\ns\u00edmbolo de cola\u00e7\u00e3o &lt;S1D000&gt;..&lt;S1D35F&gt;\ns\u00edmbolo de cola\u00e7\u00e3o &lt;SFFFF&gt; % Garantido maior valor de s\u00edmbolo. Manter no final desta lista\n...\nelemento de cola\u00e7\u00e3o &lt;U0413_0301&gt; de &quot;&lt;U0413&gt;&lt;U0301&gt;&quot;\nelemento de cola\u00e7\u00e3o &lt;U0413_0341&gt; de &quot;&lt;U0413&gt;&lt;U0341&gt;&quot;<\/code><\/pre>\n<p><\/p>\n<ul>\n<li><em>collating-symbol<\/em> registra a string <em>OSMANYA<\/em> na tabela de nomes de pesos <\/li>\n<li><em>collating-symbol ..<\/em> registra uma sequ\u00eancia de nomes composta por um prefixo <em>S<\/em> e um sufixo num\u00e9rico hexadecimal de <em>1D000<\/em> at\u00e9 <em>1D35F<\/em>.<\/li>\n<li><em>FFFF<\/em> em <em>collating-symbol<\/em> parece um grande inteiro sem sinal na representa\u00e7\u00e3o hexadecimal, mas <em>&lt;SFFFF&gt;<\/em> \u00e9 apenas um nome que poderia parecer como <em>&lt;VERYBIGVAL&gt;<\/em> <\/li>\n<li>nome <em>&lt;U0413&gt;<\/em> significa um ponto de c\u00f3digo na codifica\u00e7\u00e3o <em>UCS-4<\/em><\/li>\n<li><em>elemento de cola\u00e7\u00e3o &lt;U0413_0301&gt; de &quot;&lt;U0413&gt;&lt;U0301&gt;&quot;<\/em> registra um novo nome para um par de pontos Unicode. <\/li>\n<\/ul>\n<p><\/p>\n<p>Quando os nomes dos pesos s\u00e3o definidos, s\u00e3o estabelecidos os pr\u00f3prios pesos. Como na compara\u00e7\u00e3o apenas as rela\u00e7\u00f5es de maior e menor importam, os pesos s\u00e3o determinados por uma simples sequ\u00eancia de enumera\u00e7\u00e3o dos nomes. Pesos mais &quot;leves&quot; s\u00e3o listados primeiro, seguidos dos mais &quot;peseados&quot;. Lembro que a cada caractere Unicode \u00e9 atribu\u00eddo quatro pesos diferentes. Aqui eles s\u00e3o reunidos em uma \u00fanica sequ\u00eancia ordenada. Teoricamente, qualquer nome simb\u00f3lico pode ser utilizado em qualquer um dos quatro n\u00edveis, mas os coment\u00e1rios indicam que os desenvolvedores mentalmente dividem os nomes por n\u00edveis.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">% Atribui\u00e7\u00f5es de peso simb\u00f3lico\n\n% Atribui\u00e7\u00f5es de peso de terceiro n\u00edvel\n\n\n\n\n...\n% Atribui\u00e7\u00f5es de peso de segundo n\u00edvel\n\n % LINHA BAIXA COMBINANTE\n % VINHO COMBINANTE ACIMA\n % VINHO INVERTIDO COMBINANTE ACIMA\n...\n% Atribui\u00e7\u00f5es de peso de primeiro n\u00edvel\n % TABULA\u00c7\u00c3O HORIZONTAL \n % QUEBRA DE LINHA\n % TABULA\u00c7\u00c3O VERTICAL\n...\n % LETRA MIN\u00daSCULA CYRILLIC DE\n % LETRA MIN\u00daSCULA CYRILLIC KOMI DE\n % LETRA MIN\u00daSCULA CYRILLIC DJE\n % LETRA MIN\u00daSCULA CYRILLIC KOMI DJE\n % LETRA MIN\u00daSCULA CYRILLIC GJE\n % LETRA MIN\u00daSCULA CYRILLIC ZE COM DESCENSOR\n % LETRA MIN\u00daSCULA CYRILLIC IE\n % LETRA MIN\u00daSCULA CYRILLIC IE COM BREVE\n % LETRA MIN\u00daSCULA CYRILLIC UE UCRANIANO\n % LETRA MIN\u00daSCULA CYRILLIC ZHE<\/code><\/pre>\n<p><\/p>\n<p>Finalmente, a tabela de pesos propriamente dita.<\/p>\n<p><\/p>\n<p>A se\u00e7\u00e3o de pesos est\u00e1 contida em linhas com palavras-chave <em>order_start<\/em> e <em>order_end<\/em>. Par\u00e2metros adicionais <em>order_start<\/em> definem em que dire\u00e7\u00e3o as linhas s\u00e3o visualizadas em cada n\u00edvel de compara\u00e7\u00e3o. Por padr\u00e3o, \u00e9 utilizado o par\u00e2metro <em>forward<\/em>. O corpo da se\u00e7\u00e3o consiste em linhas que cont\u00eam o c\u00f3digo do s\u00edmbolo e seus quatro pesos. O c\u00f3digo do s\u00edmbolo pode ser representado pelo pr\u00f3prio s\u00edmbolo, pelo ponto de c\u00f3digo ou pelo nome simb\u00f3lico definido anteriormente. Os pesos tamb\u00e9m podem ser definidos por nomes simb\u00f3licos, pontos de c\u00f3digo ou pelos pr\u00f3prios s\u00edmbolos. Se forem utilizados pontos de c\u00f3digo ou s\u00edmbolos, seu peso corresponde ao valor num\u00e9rico do ponto de c\u00f3digo (posi\u00e7\u00e3o na tabela Unicode). Os s\u00edmbolos n\u00e3o explicitamente listados (na minha compreens\u00e3o) s\u00e3o considerados como atribu\u00eddos \u00e0 tabela com um peso prim\u00e1rio igual \u00e0 posi\u00e7\u00e3o na tabela Unicode. O valor especial de peso <em>IGNORE<\/em> significa que, no n\u00edvel de compara\u00e7\u00e3o correspondente, esse s\u00edmbolo \u00e9 ignorado.<\/p>\n<p><\/p>\n<p>Para demonstrar a estrutura dos pesos, escolhi tr\u00eas fragmentos bastante \u00f3bvios:<\/p>\n<p><\/p>\n<ul>\n<li>s\u00edmbolos que s\u00e3o completamente ignorados<\/li>\n<li>s\u00edmbolos equivalentes ao n\u00famero tr\u00eas nos dois primeiros n\u00edveis<\/li>\n<li>o in\u00edcio do alfabeto cir\u00edlico, que n\u00e3o cont\u00e9m sinais diacr\u00edticos, portanto, \u00e9 classificado principalmente pelos primeiros e terceiros n\u00edveis.<\/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>Agora podemos voltar \u00e0 ordena\u00e7\u00e3o dos exemplos do in\u00edcio do artigo. A pegadinha est\u00e1 nesta parte da tabela de pesos:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">IGNORE;IGNORE;IGNORE; % ESPA\u00c7O\n IGNORE;IGNORE;IGNORE; % PONTO DE EXCLAMA\u00c7\u00c3O\n IGNORE;IGNORE;IGNORE; % ASPAS\n...<\/code><\/pre>\n<p><\/p>\n<p>\u00c9 evidente que nesta tabela os sinais de pontua\u00e7\u00e3o est\u00e3o presentes na tabela <em>ASCII<\/em> (incluindo espa\u00e7os) ao comparar strings, geralmente \u00e9 ignorado. A \u00fanica exce\u00e7\u00e3o s\u00e3o as strings que coincidem em tudo, exceto nos sinais de pontua\u00e7\u00e3o que aparecem nas posi\u00e7\u00f5es correspondentes. As strings do meu exemplo (ap\u00f3s a ordena\u00e7\u00e3o) para o algoritmo de compara\u00e7\u00e3o s\u00e3o assim:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">AbakanovMikhailPintor\nYolkinaEllacranovreparadora\nIvanovaAllaPintor\nIvanovAndreiSerralheiro<\/code><\/pre>\n<p><\/p>\n<p>Tendo em conta que na tabela de pesos as letras mai\u00fasculas em russo v\u00eam ap\u00f3s as min\u00fasculas (no terceiro n\u00edvel <em>&lt;CAP&gt;<\/em> mais pesadas do que <em>&lt;MIN&gt;<\/em>), a ordena\u00e7\u00e3o parece absolutamente correta.<\/p>\n<p><\/p>\n<p>Ao definir a vari\u00e1vel <em>LC_COLLATE=C<\/em> \u00e9 carregada uma tabela especial que define a compara\u00e7\u00e3o byte a byte<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">static const uint32_t collseqwc[] =\n{\n  8, 1, 8, 0x0, 0xff,\n  \/* tabela de 1\u00ba n\u00edvel *\/\n  6 * sizeof (uint32_t),\n  \/* tabela de 2\u00ba n\u00edvel *\/\n  7 * sizeof (uint32_t),\n  \/* tabela de 3\u00ba n\u00edvel *\/\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>Como no Unicode o ponto de c\u00f3digo \u0401 est\u00e1 antes de \u0410, as strings s\u00e3o ordenadas de acordo.<\/p>\n<p><\/p>\n<h1 id=\"tekstovye-i-dvoichnye-tablicy\">Tabelas textuais e bin\u00e1rias<\/h1>\n<p><\/p>\n<p>\u00c9 \u00f3bvio que a compara\u00e7\u00e3o de strings \u00e9 uma opera\u00e7\u00e3o extremamente comum, enquanto o processamento da tabela <em>CTT<\/em> \u00e9 um procedimento bastante oneroso. Para otimizar o acesso \u00e0 tabela, ela \u00e9 compilada em forma bin\u00e1ria pelo comando <em>localedef<\/em>.<\/p>\n<p><\/p>\n<p>Time <em>localedef<\/em> que recebe como par\u00e2metros um arquivo com a tabela de particularidades nacionais (op\u00e7\u00e3o <em>-i<\/em>), onde todos os s\u00edmbolos s\u00e3o representados por pontos Unicode, e um arquivo de correspond\u00eancia entre pontos Unicode e s\u00edmbolos de uma codifica\u00e7\u00e3o espec\u00edfica (op\u00e7\u00e3o <em>--follow<\/em>). Como resultado da execu\u00e7\u00e3o, s\u00e3o criados arquivos bin\u00e1rios para a localidade, com o nome especificado no \u00faltimo par\u00e2metro.<\/p>\n<p><\/p>\n<p><em>Glibc<\/em> suporta dois formatos de arquivos bin\u00e1rios: &quot;tradicional&quot; e &quot;moderno&quot;.<\/p>\n<p><\/p>\n<p>O formato tradicional implica que o nome da localidade \u00e9 o nome de um subdiret\u00f3rio em <em>\/usr\/lib\/locale\/<\/em>. Neste subdiret\u00f3rio est\u00e3o armazenados arquivos bin\u00e1rios <em>LC_COLLATE<\/em>, <em>LC_CTYPE<\/em>, <em>LC_TIME<\/em> etc. O arquivo <em>LC_IDENTIFICATION<\/em> cont\u00e9m o nome formal da localidade (que pode diferir do nome do diret\u00f3rio) e coment\u00e1rios.<\/p>\n<p><\/p>\n<p>O formato moderno presume que todas as localidades sejam armazenadas em um \u00fanico arquivo <em>\/usr\/lib\/locale\/locale-archive<\/em>, que \u00e9 mapeado na mem\u00f3ria virtual de todos os processos que o utilizam <em>glibc<\/em>O nome da localidade no formato moderno \u00e9 sujeito a uma certa canoniza\u00e7\u00e3o \u2014 nos nomes de codifica\u00e7\u00e3o ficam apenas d\u00edgitos e letras, convertidos para min\u00fasculas. Assim <em>pt_BR.KOI8-R<\/em>, ser\u00e1 mantido como <em>pt_BR.koi8r<\/em>.<\/p>\n<p><\/p>\n<p>Os arquivos de entrada s\u00e3o procurados no diret\u00f3rio atual e tamb\u00e9m nos diret\u00f3rios <em>\/usr\/share\/i18n\/locales\/<\/em> e <em>\/usr\/share\/i18n\/charmaps\/<\/em> para arquivos <em>CTT<\/em> e arquivos de codifica\u00e7\u00f5es, respectivamente.<\/p>\n<p><\/p>\n<p>Por exemplo, o comando<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">localedef -i pt_BR -f MAC-CYRILLIC pt_BR.MAC-CYRILLIC<\/code><\/pre>\n<p><\/p>\n<p>compilar\u00e1 o arquivo <em>\/usr\/share\/i18n\/locales\/ru_RU<\/em> usando o arquivo de codifica\u00e7\u00e3o <em>\/usr\/share\/i18n\/charmaps\/MAC-CYRILLIC.gz<\/em> e salvar\u00e1 o resultado em <em>\/usr\/lib\/locale\/locale-archive<\/em> com o nome <em>pt_BR.maccyrillic<\/em><\/p>\n<p><\/p>\n<p>Se a vari\u00e1vel <em>LANG=en_US.UTF-8<\/em> for definida, <em>glibc<\/em> ser\u00e1 feita a busca por arquivos bin\u00e1rios de localidades na seguinte sequ\u00eancia de arquivos e diret\u00f3rios:<\/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>Se a localidade aparecer tanto nos formatos tradicionais quanto modernos, a prioridade ser\u00e1 dada ao moderno.<\/p>\n<p><\/p>\n<p>Para visualizar a lista de localidades compiladas, pode-se usar o comando <em>locale -a<\/em>.<\/p>\n<p><\/p>\n<h1 id=\"podgotovka-svoey-tablicy-sravneniya\">Preparando sua pr\u00f3pria tabela de compara\u00e7\u00e3o<\/h1>\n<p><\/p>\n<p>Agora, armado com conhecimento, voc\u00ea pode criar sua pr\u00f3pria tabela de compara\u00e7\u00e3o ideal. Esta tabela deve comparar corretamente as letras russas, incluindo a letra \u0401, e tamb\u00e9m considerar a pontua\u00e7\u00e3o de acordo com a tabela <em>ASCII<\/em>.<\/p>\n<p><\/p>\n<p>O processo de prepara\u00e7\u00e3o da sua tabela de ordena\u00e7\u00e3o consiste em duas etapas: edi\u00e7\u00e3o da tabela de pesos e compila\u00e7\u00e3o dela em forma bin\u00e1ria com o comando <em>localedef<\/em>.<\/p>\n<p><\/p>\n<p>Para que a tabela de compara\u00e7\u00e3o possa ser ajustada com o m\u00ednimo de esfor\u00e7o de edi\u00e7\u00e3o, o formato <em>ISO 14652<\/em> prev\u00ea se\u00e7\u00f5es de ajuste de pesos j\u00e1 existentes na tabela. A se\u00e7\u00e3o come\u00e7a com a palavra-chave <em>reorder-after<\/em> e a indica\u00e7\u00e3o da posi\u00e7\u00e3o ap\u00f3s a qual a substitui\u00e7\u00e3o \u00e9 feita. A se\u00e7\u00e3o \u00e9 encerrada pela linha <em>reorder-end<\/em>. Se for necess\u00e1rio corrigir v\u00e1rias partes da tabela, deve-se criar uma se\u00e7\u00e3o para cada uma delas.<\/p>\n<p><\/p>\n<p>Eu copiei novas vers\u00f5es dos arquivos <em>iso14651_t1_common<\/em> e <em>ru_RU<\/em> do reposit\u00f3rio <em>glibc<\/em> para meu diret\u00f3rio pessoal ~\/.local\/share\/i18n\/locales\/ e fiz algumas edi\u00e7\u00f5es no trecho <em>LC_COLLATE<\/em> em <em>ru_RU<\/em>. As novas vers\u00f5es dos arquivos s\u00e3o totalmente compat\u00edveis com minha vers\u00e3o <em>glibc<\/em>. Se voc\u00ea quiser usar as vers\u00f5es antigas dos arquivos, ter\u00e1 que alterar os nomes simb\u00f3licos e o local de onde come\u00e7a a substitui\u00e7\u00e3o na tabela.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">LC_COLLATE\n% Copiar o modelo do ISO\/IEC 14651\ncopiar &quot;iso14651_t1&quot;\nreordenar ap\u00f3s &lt;U000D&gt;\n&lt;U0020&gt; &lt;S0020&gt;;&lt;BASE&gt;;&lt;MIN&gt;&lt;U0020&gt; % ESPA\u00c7O\n&lt;U0021&gt; &lt;S0021&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U0021&gt; % PONTO DE EXCLAMA\u00c7\u00c3O\n&lt;U0022&gt; &lt;S0022&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U0022&gt; % ASPAS\n...\n&lt;U007D&gt; &lt;S007D&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U007D&gt; % CHAVES DIREITAS\n&lt;U007E&gt; &lt;S007E&gt;;&lt;BASE&gt;&lt;MIN&gt;&lt;U007E&gt; % TILDE\nreordenar-fim\nFIM LC_COLLATE<\/code><\/pre>\n<p><\/p>\n<p>Na verdade, eu deveria ter trocado os campos em <em>LC_IDENTIFICATION<\/em> para que eles apontassem para a localidade <em>pt_BR<\/em>, mas no meu exemplo isso n\u00e3o foi necess\u00e1rio, pois eu exclu\u00ed da busca as localidades do arquivo <em>locale-archive<\/em>.<\/p>\n<p><\/p>\n<p>Para <em>localedef<\/em> trabalhava com os arquivos na minha pasta atrav\u00e9s da vari\u00e1vel <em>I18NPATH<\/em> pode-se adicionar um diret\u00f3rio adicional para buscar arquivos de entrada, enquanto o diret\u00f3rio para salvar arquivos bin\u00e1rios pode ser fornecido como um caminho com barras:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; I18NPATH=~\/.local\/share\/i18n localedef -i pt_BR -f UTF-8 ~\/.local\/lib\/locale\/pt_BR.UTF-8<\/code><\/pre>\n<p><\/p>\n<p><em>POSIX<\/em> sup\u00f5e-se que em <em>LANG<\/em> pode-se escrever caminhos absolutos para diret\u00f3rios com arquivos de localidade que come\u00e7am com uma barra, mas <em>glibc<\/em> em <em>Linux<\/em> todos os caminhos s\u00e3o considerados a partir do diret\u00f3rio base, que pode ser redefinido atrav\u00e9s da vari\u00e1vel <em>LOCPATH<\/em>. Ap\u00f3s definir <em>LOCPATH=~\/.local\/lib\/locale\/<\/em> todos os arquivos relacionados \u00e0 localiza\u00e7\u00e3o ser\u00e3o procurados apenas na minha pasta. O arquivo de localidades com a vari\u00e1vel <em>LOCPATH<\/em> \u00e9 ignorado.<\/p>\n<p><\/p>\n<p>Aqui est\u00e1 o teste decisivo:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LANG=pt_BR.UTF-8 LOCPATH=~\/.local\/lib\/locale\/ sort buhg.txt\nAbakanov Mikhail;pintor\nYolkina Ella;guindasteira\nIvanov Andrei;encanador\nIvanova Alla;advogada<\/code><\/pre>\n<p><\/p>\n<p>Uhu! Conseguimos!<\/p>\n<p><\/p>\n<h1 id=\"rabota-nad-oshibkami\">Trabalhando nos erros<\/h1>\n<p><\/p>\n<p>J\u00e1 respondi \u00e0s perguntas sobre a ordena\u00e7\u00e3o de linhas, feitas no in\u00edcio, mas ainda restam algumas quest\u00f5es sobre erros \u2014 vis\u00edveis e invis\u00edveis.<\/p>\n<p><\/p>\n<p>Vamos voltar para a tarefa original.<\/p>\n<p><\/p>\n<p>E o programa <em>sort<\/em> e o programa <em>join<\/em> usam as mesmas fun\u00e7\u00f5es de compara\u00e7\u00e3o de strings de <em>glibc<\/em>. Como isso ocorreu que <em>join<\/em> gerou um erro de ordena\u00e7\u00e3o em linhas ordenadas pelo comando <em>sort<\/em> na localidade <em>en_US.UTF-8<\/em>? \u041e\u0442\u0432\u0435\u0442 \u043f\u0440\u043e\u0441\u0442: <em>sort<\/em> compara a string inteira, enquanto <em>join<\/em> compara apenas a chave, que por padr\u00e3o \u00e9 o in\u00edcio da string at\u00e9 o primeiro caractere de espa\u00e7o. No meu exemplo, isso levou a uma mensagem de erro, pois a ordena\u00e7\u00e3o das primeiras palavras nas linhas n\u00e3o coincidiu com a ordena\u00e7\u00e3o das strings completas.<\/p>\n<p><\/p>\n<p>Localidade <em>&quot;C&quot;<\/em> garante que nas linhas classificadas as substrings iniciais at\u00e9 o primeiro espa\u00e7o tamb\u00e9m estar\u00e3o classificadas, mas isso apenas mascarar\u00e1 o erro. \u00c9 poss\u00edvel selecionar dados como (pessoas com sobrenomes iguais, mas nomes diferentes), que sem a mensagem de erro resultariam em um resultado incorreto na fus\u00e3o de arquivos. Se quisermos que <em>join<\/em> combine linhas de arquivos por nome completo, a maneira correta seria especificar claramente o delimitador de campos e classificar pela chave prim\u00e1ria, e n\u00e3o por toda a linha. Dessa forma, a fus\u00e3o ocorrer\u00e1 corretamente e n\u00e3o haver\u00e1 erros em nenhuma localidade:<\/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>Um exemplo executado com sucesso na codifica\u00e7\u00e3o <em>CP1251<\/em> cont\u00e9m mais um erro. O fato \u00e9 que em todas as distribui\u00e7\u00f5es que conhe\u00e7o <em>Linux<\/em> faltam pacotes com a localidade compilada <em>ru_RU.CP1251<\/em>. Se a localidade compilada n\u00e3o for encontrada, ent\u00e3o <em>sort<\/em> utiliza silenciosamente a compara\u00e7\u00e3o byte a byte, que \u00e9 o que observamos.<\/p>\n<p><\/p>\n<p>Ali\u00e1s, h\u00e1 mais um pequeno bug relacionado \u00e0 indisponibilidade de localidades compiladas. O comando <em>LOCPATH=\\\/tmp locale -a<\/em> produzir\u00e1 uma lista de todas as localidades em <em>locale-archive<\/em>, mas com a vari\u00e1vel definida <em>LOCPATH<\/em> para todos os programas (incluindo o pr\u00f3prio <em>locale<\/em>) essas localidades estar\u00e3o indispon\u00edveis.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">$&gt; LOCPATH=\\\/tmp locale -a | grep en_US\nlocale: Cannot set LC_CTYPE to default locale: No such file or directory\nlocale: Cannot set LC_MESSAGES to default locale: No such file or directory\nlocale: Cannot set LC_COLLATE to default locale: No such file or directory\nen_US\nen_US.iso88591\nen_US.iso885915\nen_US.utf8\n\n$&gt; LC_COLLATE=en_US.UTF-8 sort --debug\nsort: using \u2018en_US.UTF-8\u2019 sorting rules\n\n$&gt; LOCPATH=\\\/tmp LC_COLLATE=en_US.UTF-8 sort --debug\nsort: using simple byte comparison<\/code><\/pre>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Conclus\u00e3o<\/h1>\n<p><\/p>\n<p>Se voc\u00ea \u00e9 um programador que acredita que strings s\u00e3o conjuntos de bytes, ent\u00e3o sua escolha <em>LC_COLLATE=C<\/em>.<\/p>\n<p><\/p>\n<p>Se voc\u00ea \u00e9 um linguista ou compilador de dicion\u00e1rios, ent\u00e3o \u00e9 melhor compilar sua pr\u00f3pria localidade.<\/p>\n<p><\/p>\n<p>Se voc\u00ea \u00e9 um usu\u00e1rio comum, basta se acostumar ao fato de que o comando <em>ls -a<\/em> produz arquivos que come\u00e7am com um ponto, misturados com arquivos que come\u00e7am com letras, e <em>Midnight Commander<\/em>, que usa suas fun\u00e7\u00f5es internas para classificar nomes, coloca os arquivos que come\u00e7am com ponto no in\u00edcio da lista.<\/p>\n<p><\/p>\n<h1 id=\"ssylki\">Links<\/h1>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/unicode.org\/reports\/tr10\/\">Relat\u00f3rio n\u00ba 10 do algoritmo de collation Unicode <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/www.unicode.org\/Public\/UCA\/latest\/allkeys.txt\">Pesos de caracteres no 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 implementa\u00e7\u00e3o da biblioteca para trabalhar com Unicode da IBM. <\/a><\/noindex><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/demo.icu-project.org\/icu-bin\/collation.html\">Teste de classifica\u00e7\u00e3o usando <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\">Pesos de caracteres em <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\">Descri\u00e7\u00e3o do formato do arquivo com pesos <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\">Discuss\u00e3o sobre compara\u00e7\u00e3o de strings em <em>glibc<\/em><\/a><\/noindex><\/p>\n<p>Fonte: <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.3 - 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\/pt\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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\/pt\/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\udd47Como o sort do Linux classifica strings | ProHoster","description":"Introdu\u00e7\u00e3o Tudo come\u00e7ou com um breve script que deveria combinar informa\u00e7\u00f5es sobre endere\u00e7os de e-mail dos funcion\u00e1rios, obtidas de uma lista de usu\u00e1rios da newsletter, com.","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/kak-linuxovskij-sort-sortiruet-stroki","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/83055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=83055"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/83055\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=83055"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=83055"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=83055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}