Sortera textfil på fält som kan vara i olika kolumner

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
JJ
Inlägg: 366
Blev medlem: 16 maj 2005, 21:33:02

Sortera textfil på fält som kan vara i olika kolumner

Inlägg av JJ »

Jag har en återkommande uppgift som jag inte lyckats lösa med "de vanliga" unix-komandona (grep, sort, elementär awk och sed...).

Problemet: sortera en stor textfil på talet som står efter 'id: '. Detta kan komma i olika kolumner. Filen kan vara mycker stor.
$ cat fil
af asf id: 2 af af
asdf id: 4 asdf
af id: 3 asdf
af asfd asf asdf id: 2 as

Följande lösning fungerar men den tar för lång tid (grep:a ut alla id-nummer, loopa över dessa och grep:a ut de tillhörande raderna):
for i in `cat fil | grep -oP 'id: \d+' |  grep -oP '\d+' | sort -g | uniq`;do cat fil | grep "id: $i";done

Denna lösning funkar bra för obegräsat stora filer men ger inte en sorterad fil utan en ny fil per id. Skulle iofs gå att cat:a ihop dessa till en ny stor. Och så använder den scriptspråk:
cat fil | ruby -ne 'BEGIN{f={}};i=$_[/id: (\d+)/,1];g=f;if not g then f=File.open(i, "w") end;f.puts($_)'


En lösning till som också använder scriptspråk delvis (plockar ut id-numret och lägger till det i en ny första kolumn, sorterar på första kolumnen, tar bort första kolumnen):
cat fil | ruby -ne 'print $_[/id: (\d+)/,1];print " "; puts $_' | sort -g -k 1,1 | sed 's/^[0-9]* //g'


Och en lösning till som avänder scriptspråk rakt av. (läser in alla rader i en array, sorterar arrayen) Tar en massa minne (krasch!) och är slött. unix-sort känns stabilare:
cat fil | ruby -e 'r=ARGF.readlines; r.sort!{|a,b| x=a[/id: (\d+)/, 1].to_i;y=b[/id: (\d+)/,1].to_i;x<=>y};puts r'


Jag har alltså lösningar på det men jag undrar om man måste ta till scriptspråk?

Någon som har en idé?
Senast redigerad av JJ 29 oktober 2022, 14:42:27, redigerad totalt 1 gång.
Användarvisningsbild
Glenn
Inlägg: 33666
Blev medlem: 24 februari 2006, 12:01:56
Ort: Norr om Sthlm
Kontakt:

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av Glenn »

Det värsta är att jag har löst exakt det problemet i sh/bash en gång i tiden, men jag minns inte hur jag gjorde..

Iofs kan jag ha löst det på nåt långsamt sätt med.

Ett fulsätt är ju annars att loopa över filen och plocka ut varje kolumn och skapa en ny fil med den du vill sortera på först, sortera filen och sen flytta om kolumnerna igen, men det känns ju lite som att gå över ån efter vatten, jag tror jag löste det på annat sätt.
guckrum
Inlägg: 1669
Blev medlem: 19 juni 2012, 09:04:27
Ort: Lund

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av guckrum »

Ja, usch, det var ett tråkigt filformat du måste hantera!

Här tycker jag att man ligger på gränsen till att skriva ett ordentligt
script, men en oneliner skulle kunna se ut någonting åt detta hållet:

Kod: Markera allt

cat fil | sed 's/^\(..*id: \)\([0-9][0-9]*\)\(..*\)$/\2\t\1\2\3/' | sort -n | sed 's/^..*\t//'
Jag tänkte att man extraherar id-numret och pyntar varje rad med det först,
så kan man sedan enkelt sortera raderna ("sort -n" ovan).

Första sed-röran ovan innehåller tre stycken konsekutiva delar inom
backslashade-parenteser som används för att spara det som matchas
inuti parenteserna. Det är en inledning från radens början, sedan ett
regexp som machar valfritt antal siffror i id-delen, och till sist en del
som matchar resten fram till radslut.

Matchningarna hamnar i "variablerna" \1, \2 och \3, så om man printar
dem får man tillbaka raden. Det jag gör är då att printa \2 först också,
följt av en tab som jag använder som separator (ta något som inte
redan används!). Sista sed-uttrycket tar bort pyntet till och med tab
igen.

Det här kan gå sönder på flera vis, tex om det finns andra nycklar som
slutar på "id:".

Det känns som att man skulle kunna göra något mera tokenbaserat i
awk, men då är det nog bättre att ta steget till sitt favoritscriptspråk
och göra det ordentligt!

EDIT: Din tredje lösning är väl den elegantaste tycker jag.
davidi
Inlägg: 571
Blev medlem: 13 oktober 2011, 16:45:38
Ort: Ekerö

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av davidi »

Sätt värdet efter id: som en extra kolumn först på varje rad, t ex så här med awk:

Kod: Markera allt

{
  for (col = 1; col < NF; col++) {
      if ($col == "id:") {
          print $(col + 1), $0
          break;
      }
  }
}
Nu är det enkelt att sortera. Ta bort första kolumnen efter sorteringen:

Kod: Markera allt

| sort -n | cut -d' ' -f2-
JJ
Inlägg: 366
Blev medlem: 16 maj 2005, 21:33:02

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av JJ »

Glenn, vi får hoppas att om du hittar det du gjorde i gömmorna så funkar det inte för stora filer eller något. Det vore ju retligt att ha tappat bort något rikitgt bra :-)

davidi och guckrum: Era lösningar liknade min tredje lösning men skrivna med sed och awk. Alla verkar funka men sed ger mest street cred och ruby minst :-)

Nu ingick det i frågeställningen att man fick använda "elementär sed och awk" och då undrar man om ni lyckats med det? I dagsläget skulle jag svara "nej" men du förklarar sed så att sed nästan ser enkelt ut och awk förklarar sig självt! (Båda era lösningar låg alltså över min nivå i de båda språken.) Jag återkommer med utlåtande om ett halvår, då kanske jag tycker att ni använt språket på ett elementärt sätt tack vare det jag lärt mig av er nu :-)

För det finns alltid att lära! Min första lösning var faktist att skriva ett C-program! Funkade utmärkt men...

Nu kom jag på en annan variant med inspiration av diskussionerna här:
$ cat fil | grep -oP 'id: \d+' | grep -oP '\d+' > tmp
$ paste tmp fil | sort -n -k 1,1 | cut -f2-
Helt utan scriptspråk (sed, awk, ruby...) men tvåradare och inte enradare!

(En annan sak: sort -n är lurig här, den sorterar efter alla siffror den hittar, inte bara första kolumnen. Det är inte så jag vill ha det därför 'k 1,1.)

Tack skall ni ha för svar! Grymt att se hur andra gör
guckrum
Inlägg: 1669
Blev medlem: 19 juni 2012, 09:04:27
Ort: Lund

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av guckrum »

Ja, det var en kul övning. Jag är med på hur du använder sort och
gillar att du har koll på dess options!

En annan grej är att du har koll på exekveringstid och minnesåtgång,
och tänker på det vid "designen". "sort" är det som skalar sämst,
så är det riktigt stora filer så kanske en variant som sparar på flera
filer parallellt är vettigt. Men det har du redan kollat på.

De gamla goa shell-kommandona är underbara när det kommer till
snabb och korrekt exekvering av stora datafiler! (Är det riktigt
stora filer eller om du vill har ordning på dina exekveringar
rekommenderar jag en titt på http://exax.org)

sed-scriptet kan som sagt bära sig illa åt om nycklarna har olyckliga
namn, så jag är inte helt nöjd med det.

Och det är inget fel på tvåradare. Om man stoppar sin kod i en
Makefile så är det ingen större risk att man misslyckas vid exekvering
och så har man ett "recept" sparat på fil som är enkelt att köra.

Denna boken tycker jag är bra om man vill lära sig sed:
https://www.oreilly.com/library/view/se ... 565922255/
JJ
Inlägg: 366
Blev medlem: 16 maj 2005, 21:33:02

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av JJ »

Gött, jag skall läsa igenom ditt svar noga, länkarna såg bra ut men jag har inte klickat än. Ville bara visa att jag krympt ner tvåradaren till enradare, återigen med tips och inspiration från vår diskussion här:
paste <(cat fil | grep -oP 'id: \d+' | grep -oP '\d+') <(cat fil) | sort -n -k 1,1 | cut -f2-
...vet inte om detta var bra eller dåligt dock! :shock:
:)
Nerre
Inlägg: 26654
Blev medlem: 19 maj 2008, 07:51:04
Ort: Upplands väsby

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av Nerre »

Jag höll på och pillade mycket med sed och awk innan jag insåg att deras syntax även fungerar utmärkt att använda i perl.

I mina ögon är detta precis sånt som perl från början var tänkt att lösa. Det betyder trots allt "Practical Extraction and Report Language".

Däremot vet jag inte exakt hur man bäst löser det, men lite snabbt googlade ledde fram till den här sidan: https://www.oreilly.com/library/view/pe ... 04s16.html
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43148
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av sodjan »

Jag finner det väldigt fånigt att jaga "1-rads" lösningar!
Det viktiga är, här som alltid annars, att göra läsbara lösningar
som går att underhålla i framtiden (om det nu skulle behövas).
Sen så skulle jag gå tillbaka till källan för filen och kolla om
det inte går att få ut original filen i ett rimligare format.

Finns andra frågor också.
Är det enbart värden mellan 1 och 9?
Om du har värden över 9, hur ska dessa sorteras i relation till 1-9?

Vill du ha;

1
11
13
2
21
23
3

O.s.v.?

Eller:

1
2
3
11
13
21
23

?
JJ
Inlägg: 366
Blev medlem: 16 maj 2005, 21:33:02

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av JJ »

Nerre skrev: 30 oktober 2022, 08:29:55 Jag höll på och pillade mycket med sed och awk innan jag insåg att deras syntax även fungerar utmärkt att använda i perl.

I mina ögon är detta precis sånt som perl från början var tänkt att lösa. Det betyder trots allt "Practical Extraction and Report Language".

Däremot vet jag inte exakt hur man bäst löser det, men lite snabbt googlade ledde fram till den här sidan: https://www.oreilly.com/library/view/pe ... 04s16.html
Ja perl är väl som klippt och skuret för sådana här program där inte riktigt de vanliga unix-kommandona räcker till. Perl har ju den fördelen över blandade unix-kommandon att det är portabelt, man slipper uppleva att den maskin som skall köra ens stiliga script har en version av grep som inte stöder alla flaggor som man använt. Sedan har ju perl sin eviga förbannelse, det är lite för bra för sitt eget bästa! Det som börjar som ett snabbt hack blir jättelyckat och växer till ett program som måste underhållas av andra och då skiner oftast inte perl!

Att sortera i perl påminner om min fjärde lösning i första posten där sorteringen gjordes i ruby. ruby är till stor del inspirerat av perl, namnet bland annat. En skillnad verkar vara att i perl-världen tänker man mer på att det skall gå snabbt, i ruby-världen tänker man mest på att koden skall bli snygg. Det är mitt intryck iallafall :-)
sodjan skrev: 30 oktober 2022, 11:15:18 Jag finner det väldigt fånigt att jaga "1-rads" lösningar!
Det viktiga är, här som alltid annars, att göra läsbara lösningar
som går att underhålla i framtiden (om det nu skulle behövas).
Ungefär som att jag finner det fånigt att jaga assemblerinstruktioner när det finns högnivå kompilerad kod som är så bra. Jag vet att andra tycker att det är en kul grej. (Jag brukar hålla mig ifrån sådana trådar.)

Ibland om man tex letar runt i en stor fil efter ett mönster så är det viktgt att kunna skriva kraftfulla och kompakta uttryck så att inte själva uttrycken kommer ivägen för det man letar efter. Men har inte du det behovet eller har ett annat sätt att jobba så grattis till dig!
sodjan skrev: 30 oktober 2022, 11:15:18 Sen så skulle jag gå tillbaka till källan för filen och kolla om
det inte går att få ut original filen i ett rimligare format.
Det är omöjligt.

Det är i sådana här lägen det är väldigt behändigt med enradare och fiffiga script på kommandoraden, man har en oläslig och otrevligt fil men efter att man pipe:at den genom några unix-kommandon och script så kan man läsa ut det viktigaste. Läsbart? Nej! Underhållsbart? Nej! Funkar det och man har nytta av det? Ja!
sodjan skrev: 30 oktober 2022, 11:15:18 Finns andra frågor också.
Är det enbart värden mellan 1 och 9?
Om du har värden över 9, hur ska dessa sorteras i relation till 1-9?

Vill du ha;

1
11
13
2
21
23
3

O.s.v.?

Eller:

1
2
3
11
13
21
23

?
Spelar ingen större roll men sortering i numerisk ordning (ditt första alternativ) och obegränsat stora tal är det jag använt i mina exempel.
JJ
Inlägg: 366
Blev medlem: 16 maj 2005, 21:33:02

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av JJ »

guckrum skrev: 29 oktober 2022, 23:01:47 Ja, det var en kul övning. Jag är med på hur du använder sort och
gillar att du har koll på dess options!

En annan grej är att du har koll på exekveringstid och minnesåtgång,
och tänker på det vid "designen". "sort" är det som skalar sämst,
så är det riktigt stora filer så kanske en variant som sparar på flera
filer parallellt är vettigt. Men det har du redan kollat på.

De gamla goa shell-kommandona är underbara när det kommer till
snabb och korrekt exekvering av stora datafiler! (Är det riktigt
stora filer eller om du vill har ordning på dina exekveringar
rekommenderar jag en titt på http://exax.org)

sed-scriptet kan som sagt bära sig illa åt om nycklarna har olyckliga
namn, så jag är inte helt nöjd med det.

Och det är inget fel på tvåradare. Om man stoppar sin kod i en
Makefile så är det ingen större risk att man misslyckas vid exekvering
och så har man ett "recept" sparat på fil som är enkelt att köra.

Denna boken tycker jag är bra om man vill lära sig sed:
https://www.oreilly.com/library/view/se ... 565922255/
Jag sa att jag skulle läsa ditt svar noga och sagt är sagt!

exax är nog lite för seriöst för mig i dagsläget (jag fattar inte ens riktigt vad det är :-) ) men jag lägger det på minnet! Kul grej: det är två vanliga stavelser och innehåller ett X...jag fick bara upp en massa porr när jag googlade på namnet. Kollegan fick bara upp vapen och ammunition! Den andra kollegan vågade inte testa för det hade nog blivt knark trodde han :-)

Jag vet inte om sed är en grej 2023. Eller är det kanske det? Jag tänker jag kör ruby i sådana lägen och så finns ju perl. Har sed någon mer fördel än street cred?

Men iden att köra make-filer verkar klockren! Jag har börjat göra det, när pipe-linen tar för lång tid att köra så hamnar den i Makefile och gör en temp-fil som jag kan fortsätta pilla med. Dessutom blir det någon slags dokumentation på det sättet.
guckrum
Inlägg: 1669
Blev medlem: 19 juni 2012, 09:04:27
Ort: Lund

Re: Sortera textfil på fält som kan vara i olika kolumner

Inlägg av guckrum »

sed är blixtsnabbt och använder en enkel form av regexpar som funkar "överallt", så det är väldigt lite overhead att kunna lite av det också. Förslag isf: kolla och testa printkommandot (använd det som grep) och substitutionskommandot (typ search and replace), kolla sedan vilka mer kommandon som finns såndu kan slå upp dem vid behov.

Makefiler är ett jättebra sätt att inte bara att exekvera små script, utan också, som du säger, dokumentera och spara koden för framtiden. Kul att du gillade det!
Skriv svar