Sortera med Linux

Linux är på många sätt ett charmigt operativsystem. Till att börja med är det (oftast) gratis, även om det finns skumma element som då och då försöker göra pengar på det eller delar av det. En annan klämmig sida av Linux är de många frivilliga insatser som gjorts och görs av utvecklare för att uppdatera koden och lägga till nya finesser; tycker du någon detalj saknas kan du ofta ladda ner källkoden, skriva till lite kod, och skicka in för testning och eventuell användning i en kommande release[1]. Och tycker man att ett projekt håller på att spåra ur kan man oftast »gaffla» det, dvs starta ett nytt projekt baserat på koden vid ett visst tillfälle i utvecklingen, se till exempel historien om Mambo och Joomla.

Men så är det ju det här med att sortera text under Linux. Jag har varit med i svängen under en längre tid, och har hittills inte stött på något datorsystem som varit särskilt lyckat vad det gäller sortering, men Linux tar ändå priset för det mest sofistikerade upplägget, kombinerat med det mest patetiska misslyckandet. Jag har för eget bruk hackat min Opensuse-burk så att den sorterar som jag vill, men innan jag berättar om mitt hack måste jag beskriva vari problemet ligger; hav tålamod några skärmsidor![2]

Låt oss anta att vi vill sortera följande illustrativa och informativa textmassa:

aåä  
a äå 
å aä 
å äa 
a åä 
aäå  
aå ä 
ä aå 
aä å 

Om jag nu, med rosor på kind och solsken i blick, skriver sort fil, så sorterar datorn om texten på nedanstående sätt:

å äa 
ä aå 
a äå 
aä å 
aäå  
å aä 
a åä 
aå ä 
aåä  

Ärligt talat är det inte självklart att texten faktiskt är sorterad och inte randomiserad på något sätt; visserligen har jag »amerikansk engelska» som default, men måste den använda en så bisarr sorteringsalgoritm? Om någon vill försöka lista ut hur sorteringen gått till, så gömmer jag lösningen i den kommande fotnoten; men jag kommer strax att avslöja en av »finesserna» i sorteringen, så i så fall bör ni klura ut det innan ni läser vidare.[3].

Vad händer då om man använder »lokalen» (min översättning av det engelska »locale») för svenska? Lokalen för sortering styrs av miljövariabeln LC_COLLATE, och för att för att få svensk sortering kan man enklast skriva LC_COLLATE=sv_SE.UTF-8 sort fil, så får man svensk sortering bara för detta enstaka kommando[4]. Nu blir resultatet som följer:

aåä  
a åä 
aå ä 
aäå  
a äå 
aä å 
å aä 
å äa 
ä aå

Det börjar likna något, eller hur? Ordningen mellan a, å och ä är fixad, men det finns fortfarande ett problem — blankslag hanteras som om de inte existerade, vilket gör sorteringen fel i mina ögon. Ärligt talat vet jag inte hur Svenska Akademin, eller vad det nu kan vara för instans som standardiserar svenska sorteringar, ser på saken, men för mitt ändamål, ett KWIC-index över Anders F Rönnbloms texter, kändes metoden att ignorera blankslag helt bakvänd.

En snabb googling avslöjar att jag inte är den ende att förfasa mig över detta, men som så ofta är de som frågar och svarar engelsktalande, och den lösning de flesta nöjer sig med är att använda kollationeringsordningen »C», som återskapar hur datorer sorterade på stenåldern, då bara engelska talades i datorvärlden. Och då ser resultatet ut så här:

a äå 
a åä 
aä å 
aäå  
aå ä 
aåä  
ä aå 
å aä 
å äa

Om vi undantar det lilla faktum att »å» och »ä» blivit omkastade så är det så här jag skulle vilja att det ser ut.

Det »traditionella sättet»[5] att lösa problemet är att på något sätt mappa om texten så att »å» blir »ä» och vice versa, och sedan mappa tillbaka efter sorteringen; seds kommando »y» är som gjort för denna typ av hack. Men den här gången[6] beslöt jag mig för att försöka göra detta enligt Linux alla regler.

För att göra en lång historia kort, jag har ännu inte lyckats. Såvitt jag förstår borde det gå att lösa med de metoder som man använder för att mappa om »basfilen» till lokaler som till exempel den svenska, men jag har inte hittat något sätt att göra det. Huvudproblemet är att alla omdefinieringar jag sett inleds med kommandot »reorder-after», men space-tecknet, det som jag vill ändra, är definierat som det första tecknet i kollationeringsordningen. Jag har därför gått in i den bamsiga huvudfilen och ändrat på egenskaperna hos det allra första tecknet i sorteringsordningen, vilket känns ungefär som att göra en månghundraåriga ek en decimeter kortare genom att kapa den alldeles intill roten, ta av en decimeter på stammen, och sedan limma ihop rasket igen. Eller, som Otis Gibbs uttrycker det i sin fantastiska poddradio »Thanks for giving a damn»: I’d like to say up front that I haven’t the slightest idea what I’m doing, but I decided to do it anyway.

Här är mitt recept för att fixa problemet:

1. Leta rätt på filen iso14651_t1_common; under OpenSuse 12.3 ligger den i foldern /usr/share/i18n/locales, men det kan säkert vara annorlunda i andra distributioner. Öppna filen med lämplig editor[7], och gå till den rad som ser ut så här:

<U0020> IGNORE;IGNORE;IGNORE;<U0020> # 32 <SP>

Ändra denna till nedanstående:

#<U0020> IGNORE;IGNORE;IGNORE;<U0020> # 32 <SP>
<U0020> <BAS>;<BAS>;<MIN>;IGNORE   # 32 <SP>

Den första raden är naturligtvis bara originalet, sparat som en kommentar för att hålla reda på hur det såg ut innan ändringen; det är en god programmerarpraxis att behålla gammal kod ifall den nya visar sig innebära oväntade katastrofer. Den andra raden utför själva ändringen, och betyder, om jag förstått det rätt, att blanktecknet inte totalt ignoreras, som i raden ovan, utan sorteras som tecknet »BAS», vilket egentligen betyder »utan diakritiska tecken» och är definierat bland diverse arabiska diakritiska tecken; min åsikt är att det torde vara svårt att hitta ett tecken som är mer kemiskt rent på dylika än just blankslaget, och därför valde jag att återanvända detta snarare än att skapa ett nytt tecken.

2. Kompilera en ny lokal med kommandot

localedef -i sv_SE -f UTF-8 sv_SE.UTF-8

Sådär! Nu blir äntligen resultatet som förväntat:

a åä 
a äå 
aå ä 
aåä  
aä å 
aäå  
å aä 
å äa 
ä aå 

Implementeras på egen risk; inga garantier lämnas…


Fotnoter:

  1. En favorit är den tidszonsdatabas som alla Linux-system använder, och som innehåller en fantastisk mängd ihopsamlade uppgifter från runt om i världen. Ett exempel på den akribi som deltagarna ådagalägger, och som dokumenteras via kommentarer i koden: »During World War II, Germany maintained secret manned weather stations in East Greenland and Franz Josef Land, but we don’t know their time zones. My source for this is Wilhelm Dege’s book mentioned under Svalbard.» []
  2. Som den i hackermentalitet insatte säkert misstänkt är det faktiskt andra gången jag implementerar detta hack, det första hacket skrevs över vid en uppdatering; denna blogga är ett försök att korta av implementeringstiden för det tredje hacket, och därefter följande hack. []
  3. För att få denna sortering måste man göra tre saker: 1. Alla blankslag ignoreras, dvs sorteras som om de inte existerade; 2. I fas ett betraktar man alla a-liknande tecken som »a», vilket tillsammans med punkt 1 gör att alla nio raderna i min testfil förvandlas till »aaa»; 3. I fas 2 tas hänsyn till de diakritiska tecknen i ordningen »a», »å», »ä», men strängarna sorteras bakifrån; detta ger den ordning som visas ovan. []
  4. Under förutsättning att man anväder unicode, närmare bestämt dess kodning UTF-8. För att gå händelserna lite i förväg så var mitt beslut att konvertera en nyckelfil till UTF-8 från ISO 8859-1 den huvudsakliga anledningen till att jag övergav »det traditionella sättet» som jag nämner nedan. []
  5. Det vill säga, det var så jag löste detta problem på den gamla goda ABC800-tiden i mitten av 80-talet. Metoden fick sedan hänga med in i Unix-tiden, och jag använde den till och med i den första versionen av f-indexet för sådär tio år sedan. []
  6. Eller, mer korrekt — den förra för ett par år sedan, om ni är med på fotnoterna. []
  7. Härmed avses naturligtvis emacs, och inte vi. []

Kommentera

E-postadressen publiceras inte. Obligatoriska fält är märkta *