2012-10-23 1 views
7

Ich habe eine Datendatei, die ich mit regulären Ausdrücken transformieren muss. Genauer gesagt muss ich die ersten 6 Spalten gleich beibehalten und ab der 7. Spalte nur die ungeraden Spalten auswählen und dann die Felder jedes Paars aufeinanderfolgender Zeilen zusammensetzen. Ich weiß, es klingt ein bisschen kompliziert, also werde ich das an einem Beispiel verdeutlichen. Dieses ist meine ursprüngliche Datendatei (es könnte eine beliebige Anzahl von Spalten):Wählen Sie ungerade Spalten, dann Felder aufeinanderfolgender Zeilen zusammen

A B C D E F 11 12 13 14 15 16 17 18 
A B C D E F 21 22 23 24 25 26 27 28 
A B C D E F 31 31 33 34 35 36 37 38 
A B C D E F 41 42 43 44 45 46 47 48 
A B C D E F 51 52 53 54 55 56 57 58 
A B C D E F 61 62 63 64 65 66 67 68 
A B C D E F 71 72 73 74 75 76 77 78 
A B C D E F 81 82 83 84 85 86 87 88 

ich ich herausgefunden, die ersten 6 Spalten halten und dann mit

awk '{for (i = 1; i <= NF; i++) if (i < 7 || i % 2 == 1) printf $i OFS}; {print ""} 

ist dies die die ungeraden löschen Ergebnis:

A B C D E F 11 13 15 17 
A B C D E F 21 23 25 27 
A B C D E F 31 33 35 37 
A B C D E F 41 43 45 47 
A B C D E F 51 53 55 57 
A B C D E F 61 63 65 67 
A B C D E F 71 73 75 77 
A B C D E F 81 83 85 87 

Aber danach muss ich die Felder jedes Paar von aufeinanderfolgenden Reihen zusammen, wie folgt aus:

Ich dachte an sed oder awk, um den ganzen Prozess zu machen, da meine Datendateien riesig sind und ich sie effizient transformieren muss, aber ich konnte auch keinen Weg finden, die zweite Transformation durchzuführen. Jede Hilfe würde sehr geschätzt werden.

Antwort

3

Hier ist eine Möglichkeit GNU awk verwenden. Lauf wie:

awk -f script.awk file.txt 

Inhalt von script.awk:

{ 
    getline line 
    split(line, array) 
    k = 6 
    n = ((NF - k) % 2 == 0) ? 1 : 0 

    for (i=1; i<=k; i++) { 
     printf $i OFS 
    } 

    for (j=7; j<=NF-n; j+=2) { 
     x = $j OFS array[j] 
     printf (j < NF - n) ? x OFS : x "\n" 
    } 
} 

Ergebnisse:

A B C D E F 11 21 13 23 15 25 17 27 
A B C D E F 31 41 33 43 35 45 37 47 
A B C D E F 51 61 53 63 55 65 57 67 
A B C D E F 71 81 73 83 75 85 77 87 
+0

Ich schätze deine Antwort sehr, genau das habe ich gesucht. Ich werde sowohl Ihre Lösung als auch die, die ich gerade erfunden habe, ausprobieren, um zu testen, welche schneller ist (obwohl auf den ersten Blick Ihre besser ist). – Serchu

+1

@Serchu: Ich habe meine Antwort bearbeitet. Ich habe es etwas allgemeiner (und etwas kryptischer) gemacht. Es behandelt jetzt Dateien mit einer geraden Anzahl von Spalten oder einer ungeraden Anzahl von Spalten. Sie können sogar die Anzahl der Anfangsspalten festlegen, die beibehalten werden sollen. HTH. – Steve

+0

Ich verstehe die Versuchung, getline hier zu verwenden, aber es ist fast immer am besten, es zu vermeiden, da es einfache Anforderungsänderungen schwer zu implementieren macht (unter vielen anderen Vorbehalten - siehe http://awk.info/?tip/getline). Was, wenn Sie beispielsweise zusätzlich alle Zeilen mit 45 zählen möchten? Mit einer non-getline-Lösung würden Sie einfach "/ 45/{C++}" zur awk body hinzufügen, aber mit einer getline-Lösung müssen Sie das tun und "if (line ~/45 /) {C++}" nach der getline, komplizieren Dinge und erstellen doppelten Code für nur eine kleine, konzeptionell triviale Anforderungsänderung. –

2

Try this:

# d.awk 
{ 
    if (NR % 2 == 1) { 
     a = $7 
     b = $9 
     c = $11 
     d = $13 
    } else { 
     print $1, $2, $3, $4, $5, $6, a, $7, b, $9, c, $11, d, $13 
    } 
} 

Ergebnis:

% gawk -f d.awk data 
A B C D E F 11 21 13 23 15 25 17 27 
A B C D E F 31 41 33 43 35 45 37 47 
A B C D E F 51 61 53 63 55 65 57 67 
A B C D E F 71 81 73 83 75 85 77 87 
2

Perl Lösung:

perl -ane ' 
    BEGIN { $, = " " } 
    if ($. % 2) { 
     @p = (@F[0..5], @F[grep 1-$_ % 2, 6 .. $#F]) 
    } else { 
     print @p[0..5], (map { $p[$_], $F[2 * $_ - 6] } 6 .. $#F), "\n" 
    }' 
+1

Ah, Perl. Immer eine Freude für die Augen :) –

+2

@Tichodroma: Im Gegensatz zu Ihrer Lösung funktioniert es für eine beliebige Anzahl von Spalten wie in der Frage angegeben. – choroba

+0

Richtig, aber nicht erforderlich :) –

0

ich mit diesem kommen:

{ 
    if (NR % 2 == 1){ 
     for(i = 7; i <= NF; i += 2){ 
      array[i] = $i 
     } 
    } 
    else{ 
     printf "%s %s %s %s %s %s", $1, $2, $3, $4, $5, $6 
     for(i = 7; i <= NF; i += 2){ 
      printf " %s %s", array[i], $i 
     } 
     print "" 
    } 
} 

Es für das Beispiel der Werke Eröffnungsbeitrag, mit einer beliebigen Anzahl von Feldern. Meine einzige Sorge ist, dass meine tatsächlichen Dateien 2774938 Felder enthalten, und da ich neu bei afk bin, weiß ich nicht, ob dies ein effizienter Weg ist.

0
awk ' 
NR%2 { split($0,a); next } 
{ 
    for(i=7;i<NF;i+=2) { 
     $(i+1) = $i 
     $i = a[i] 
    } 
} 
1' file 

oder wenn Sie es vorziehen, eine „nette“ Lösung mit einigen Einschränkungen (die aber mit den Beispieldaten geschrieben wird funktionieren):

awk ' 
!(NR%2) { printf fmt,$7,$9,$11,$13 } 
{ for (i=8;i<=NF;i+=2) $i="%s"; fmt=$0"\n" } 
' file 
0

Dies könnte für Sie arbeiten (GNU sed):

sed -r 's/(\s?\S+)\s\S+/\1/4g;h;s/.*//;N;s/(\s?\S+)\s\S+/\1/4g;H;g;s/^(.*)(.*\n)\n\1/\1\n\2/;h;s/[^\n]*\n//;:a;s/([^ \n]*)\n([^ \n]*)/\n\2 \1\n/g;s/\n \n?| \n/\n/g;/\n[^\n ]*$/!ba;y/\n/ /;H;x;s/\n.*\n//' file