PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : SED - Frage zu bearbeitung von Messdaten



MC3330
30-03-2009, 22:50
Hallo,

ich habe eine Messreihe welche Windrichtungen anzeigt.
Ich habe dabei 36 mit Werten von 0 bis 360 Grad. Das Problem ist, dass der Logger des Messgerätes scheinbar nicht richtig programmiert ist und hin und wieder mal Leerzeichen verschluckt.

Das sieht dann ungefähr so aus

340.12 356.32182.34 360.12
Ich müßte es irgendwie schaffen daraus folgendes zu machen.

340.12 356.32 182.34 360.12


http://www.mrunix.de/forums/showthread.php?t=63206
Ich hatte esvor einiger Zeit schon mal probiert, allerdings hat die Lösung nur einen Teil meines Problems gelöst.

sed -e 's/\.[0-9][0-9][0-9][0-9][0-9]/\0 /g'

So kann ich eine Reihe von 5 Aufeinanderfolgende belibigen Ziffern ersetzen.
Ich müßte es aber irgendwie schaffen sie wieder durch sich selbst zu ersetzten, nur halt mit Leerzeichen.
Ich habe schon alle möglichen Versuche wie das hier gestartet und mich durch Tausend Foren und Newsgroupbeiträge geklickt, aber ich kriege es einfach nicht hin.


sed -e 's/\.[0-9][0-9][0-9][0-9][0-9]/\.[0-9][0-9]\0[0-9][0-9][0-9] /g'

Falls mir jemand helfen könnte, würde er mich sehr glücklich machen.

Viele Grüße Malte

ContainerDriver
31-03-2009, 09:58
Hallo,

du kannst mit \1 ... \9 auf Teilausdrücke referenzieren, die auf den Suchstring gepasst haben und in Klammern standen.
Hier mal meine Lösung (ich habe angenommen, dass es immer zwei Nachkommastellen gibt; ist das wirklich so?):


$ echo "340.12 356.32182.34 360.12"|sed -r 's/([0-9]{1,3}\.[0-9][0-9]) ?/\1 /g'
340.12 356.32 182.34 360.12


Gruß, Florian

MC3330
31-03-2009, 10:19
Hallo Florian,

vielen Dank für deine Hilfe. Das mit den zwei Nachkommastellen ist zwar leider nicht so. Aber es ist auf jeden Fall mal ein Anfang.
Ich werde das gleich mal ausprobieren.


sed -r 's/([0-9]{1,3}\.[0-9][0-9]) ?/\1 /g'


Zum Verständniss, damit ich da selber noch ein bisschen dran rumbasteln kann.
Du ersetzt ganz SED-klassich "([0-9]{1,3}\.[0-9][0-9] ?" durch "\1", richtig soweit?

"\1" enhält dabei den Ausdruck in der Klammer?

Ersetzt er dann nicht eigentlich den Ausdruck der "\1" zugeordnet worden ist mit "\"

"[0-9]{1,3}" interpretiert SED als 3 aufeinander folgende Ziffern?
Was genaue bedeutet "\.[0-9][0-9]) ?/" in der SED-Syntax?

Viele Grüße Malte

ContainerDriver
31-03-2009, 10:47
Hallo Florian,

vielen Dank für deine Hilfe. Das mit den zwei Nachkommastellen ist zwar leider nicht so. Aber es ist auf jeden Fall mal ein Anfang.
Ich werde das gleich mal ausprobieren.



Okay, dann wird es vermutlich etwas komplizierter, zumindest wird der sed-Ausdruck etwas länger. Da muss ich erstmal ein bisschen drüber nachdenken. :D

EDIT: also wenn hinterm Komma auch nur eine Ziffer stehen kann, dann gibt es ein Problem: wie soll z.B. der Fall


123.34 23.251.22 55.33

behandelt werden? Sind es 23.25 und 1.22 Grad oder 23.2 und 51.22 Grad?




sed -r 's/([0-9]{1,3}\.[0-9][0-9]) ?/\1 /g'


Zum Verständniss, damit ich da selber noch ein bisschen dran rumbasteln kann.
Du ersetzt ganz SED-klassich "([0-9]{1,3}\.[0-9][0-9]) ?" durch "\1", richtig soweit?

Genau, der String, auf den Ausdruck gepasst hat, wird durch das ersetzt, was durch den Ausdruck in der Klammer beschrieben wird.



"\1" enhält dabei den Ausdruck in der Klammer?

Ja.



Ersetzt er dann nicht eigentlich den Ausdruck der "\1" zugeordnet worden ist mit "\"


Nein, wieso sollte er? Das verstehe ich nicht ganz?



"[0-9]{1,3}" interpretiert SED als 3 aufeinander folgende Ziffern?

Es können drei aufeinanderfolgende Ziffern sein, aber auch nur eine Ziffer oder zwei Ziffern.


Was genaue bedeutet "\.[0-9][0-9]) ?/" in der SED-Syntax?


Also die Klammer gruppiert Ausdrücke. Das "\." meint einen Punkt, weil "." normalerweise für jedes beliebige Zeichen steht (deshalb wurde er mit dem \ escaped). [0-9] ist eine Ziffer von 0-9 und " ?" sagt: es darf maximal ein Leerzeichen stehen (aber auch gar keines).

Ich empfehle dir, erstmal die Manpage von sed zu lesen und dann nochmal auf http://de.wikipedia.org/wiki/Regexp zu schauen.

MC3330
31-03-2009, 13:08
Okay, dann wird es vermutlich etwas komplizierter, zumindest wird der sed-Ausdruck etwas länger. Da muss ich erstmal ein bisschen drüber nachdenken. :D

EDIT: also wenn hinterm Komma auch nur eine Ziffer stehen kann, dann gibt es ein Problem: wie soll z.B. der Fall


123.34 23.251.22 55.33

behandelt werden? Sind es 23.25 und 1.22 Grad oder 23.2 und 51.22 Grad?
[QUOTE]

Damit hast du sehr schön mein Hauptproblem getroffen. Leider werde ich die Daten nicht perfekt hinkriegen, aber ich möchte retten was zu retten ist.

Weiter Frage ich habe mal versucht deine Lösung auszuprobieren.

[QUOTE]sed (-r) 's/([0-9]{1,3}\.[0-9][0-9]) ?/\1 /g' ../../daten/raw_data/wtr/wtr_hw_d_c_raw_3.ssv > ../../daten/raw_data/wtr/wtr_hw_d_c_raw_4.ssv

Ich kriege dann die Fehlermeldungsed: illegal option -- r


sed: illegal option -- r
usage: sed script [-Ealn] [-i extension] [file ...]
sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]

oder wenn ich das r weglasse

sed: 1: "s/([0-9]{1,3}\.[0-9][0- ...": \1 not defined in the RE

ContainerDriver
31-03-2009, 15:01
Was ist das denn für eine sed-Version und auf welchem Betriebssystem? Kannst du mal statt dem -r ein -E probieren, bzw. die Klammern und das ? escapen, also:


sed 's/\([0-9]\{1,3\}\.[0-9][0-9]\) \?/\1 /g' ../../daten/raw_data/wtr/wtr_hw_d_c_raw_3.ssv > ../../daten/raw_data/wtr/wtr_hw_d_c_raw_4.ssv

MC3330
31-03-2009, 15:08
Wenn ich eine -e eingeben kriege ich das hier


sed: 1: "s/([0-9]{1,3}\.[0-9][0- ...": \1 not defined in the RE


Betriebssystem ist ein MacOS. Die SED-Version ist die , die in MacOS mit drin ist, allerdings weiß ich nicht genau wie ich rausfinde, was das für einer Version ist.

ContainerDriver
31-03-2009, 15:24
Hast du -e oder -E probiert? Geht die Version aus Posting #6, bzw. die hier:


sed 's/\([0-9]\{1,3\}\.[0-9][0-9]\) \{0,1\}/\1 /g' ../../daten/raw_data/wtr/wtr_hw_d_c_raw_3.ssv > ../../daten/raw_data/wtr/wtr_hw_d_c_raw_4.ssv

MC3330
31-03-2009, 16:22
Ich hatte -e probiert. Ich habe es gerade noch mal mit -E gemacht, da läuft er auf jeden Fall ohne Fehlermeldung.

Vielen Dank für deine Hilfe. Ich werde mal schauen was die Ergebnisse so taugen.

P.s. stimmst du mir zu, dass bei Einträgen wie 234.5234.34 die beiden Werte nicht eindeutig definiert sind? Ich habe noch ein Programm dass ectra für den Datensatz geschrieben wurde und das läd ihn Problemlos und merkt nicht an, das irgendwas nicht stimm.

ContainerDriver
31-03-2009, 16:29
Vielleicht ist da noch irgendwo ein nicht sichtbares Steuerzeichen? Kannst du die Datei mit den Messwerten vielleicht mal zur Verfügung stellen?

MC3330
31-03-2009, 16:37
Das mit dem Steuerzeichen kann eigentlich eher nicht sein, das sind ganz normale Textfiles. Falls mal eins da war, ist es mittelweile vermutlich auch verschwunden. Was du geerde siehst ist da letzte Punkte an dem ich nicht mehr weiter komme ich habe davor schon massig Korrekturen gemacht. Ich schaue heute abend mal nach, der ursprüngliche Datensatz ist allerdings ein paar GB groß, da da noch ne Menge andere Kram drinstand.

undefined
31-03-2009, 17:45
Warum so aufwendig?
Du willst nur die Leerzeichen wieder Herstellen - oder nicht?
Ich würde hier wie folgt vorgehen.
Suche NICHT Leerzeichen vor 3 Zahlen und einem Punkt => füge ein.


echo "340.12 356.32182.34 360.12
340.12 356.32 182.34 360.12
340.12 356.32182.34 360.12
340.12 356.3218 2.34 360.12
340.12 356.32 182.34 360.12
340.12 356.32 182.34 360.12
340.12 356.32182.34 360.12
340.12 356.32182.34 360.12
340.12 356.321 82.34 360.12
340.12 356.32182.34 360.12
" > /tmp/test.txt

perl -pi -e 's,(\S)([0-9]{3}\.)+,$1 $2,g' /tmp/test.txt

cat /tmp/test.txt

Edit: Die Regexp'e die oben verwendet sind viel zu gierig.

ContainerDriver
31-03-2009, 18:18
Edit: Die Regexp'e die oben verwendet sind viel zu gierig.

Inwiefern?

undefined
31-03-2009, 18:22
Weil du alles durchsuchst was Zahl{3}.Zahl{2} ist.
In meinem wird nur nach <KeinLeerzeichen>Zahl{3}. gesucht.
Erheblich schneller. ;)

MC3330
01-04-2009, 08:30
Hallo nochmal,

danke auch für deine Hilfe undefined. Ich habe bisher die Lösung von ContainerDriver ausprobiert. Das funktioniert so weit schon mal ganz gut. Vielen Dank dafür.

Jetzt hätte ich noch eine weitere Frage. Manchmal habe ich ja Blöcke von 4 Ziffern hinter den Buchstaben. Dabei gibt es zwei Fälle.

1)


23.3245.34
Könnte sowohl

23.32 45.34
als auch

23.3 245.34
bedeuten


2)


23.3545.34
bedeutet eindeutig

23.35 45.34
weil es keine Windrichtungen übder 360 Grad gibt ist

23.3 545.34
nicht möglich.

Ist es irgendwie möglich per SED Bedingung die folgenden 3 Schritte durchzuführen?
1) Wenn ein Block ".[0-9][0-9][0-9][0-9]." auftritt und die zweite Ziffer über nach dem Punkt < 4 ist, dann soll der gesamte Block inklusive der der Zeichen vor und nach den beiden Punkten bis zu den nächsten Leerzeichen durch den Eintrag "-9999 -9999" ersetzt werden.

2) Wenn ein Block ".[0-9][0-9][0-9][0-9]." auftritt und die zweite Ziffer über nach dem Punkt > 3 ist, dann soll nach der zweiten Zifferein Leerzeichen eingefügt werden also ".[0-9][0-9] [0-9][0-9]."

3) Wenn ein Block ".[0-9][0-9][0-9][0-9][0-9]." auftritt, dann soll nach der zweiten Ziffer ein Leerzeichen eingefügt werden also ".[0-9][0-9] [0-9][0-9][0-9]."

Das wäre die absolute Ideallösung für mich, was man aus diesen Daten noch machen kann. Schritt 3 ist jetzt schon gelöst. Schritt 2 wäre zwar toll aber nicht unbedingt notwendig. Schritt 1 wäre noch recht wichtig wenn der klappen könnte.


Ist es irgendwie wöglich, dass man in SED als Bedingung einbaut.
1) Block zwischen den Punkten 4 Stellen und 2 Stelle > 3

jan61
01-04-2009, 20:30
Moin,

Du kannst die Ranges genauer definieren:


jan@jack:~/tmp/wind> cat data
340.12 356.32182.34 350.12 # eindeutig: 5 Stellen trennen nach 2. NK-Stelle
340.34 33.1234.22 12.12 # nicht eindeutig (33.1 234.22 oder 22.12 34.22) -> -9999
340.34 33.1359.99 12.12 # dito (33.13 59.99 oder 33.1 359.99)
340.34 33.1360.00 12.12 # dito (33.13 60.00 oder 33.1 360.00)
340.34 33.1360.01 12.12 # eindeutig: 33.13 60.01
340.34 33.1361.01 12.12 # eindeutig: 33.13 61.01
340.34 33.1400.00 12.12 # eindeutig: 33.14 00.00

jan@jack:~/tmp/wind> cat repair.sed
s/ [0-9]*\.[0-9][0-3]60\.0* / -9999 -9999 /g
s/ [0-9]*\.[0-9][0-3][0-5][0-9]\.[0-9]* / -9999 -9999 /g
s/\(\.[0-9][0-9]\)\([0-9][0-9]\.\)/\1 \2/g
s/\(\.[0-9][0-9]\)\([0-9][0-9][0-9]\.\)/\1 \2/g
jan@jack:~/tmp/wind> sed -f repair.sed data
340.12 356.32 182.34 350.12 # eindeutig: 5 Stellen trennen nach 2. NK-Stelle
340.34 -9999 -9999 12.12 # nicht eindeutig (33.1 234.22 oder 22.12 34.22) -> -9999
340.34 -9999 -9999 12.12 # dito (33.13 59.99 oder 33.1 359.99)
340.34 -9999 -9999 12.12 # dito (33.13 60.00 oder 33.1 360.00)
340.34 33.13 60.01 12.12 # eindeutig: 33.13 60.01
340.34 33.13 61.01 12.12 # eindeutig: 33.13 61.01
340.34 33.14 00.00 12.12 # eindeutig: 33.14 00.00
Das Script repair.sed führt nacheinander mehrere Ersetzungen durch, von den Spezialfällen hin zu den allgemeineren. Damit können die nachfolgenden Bedingungen auch unschärfer formuliert werden.

Jan

MC3330
02-04-2009, 09:02
Jan, du bist ein Genie. Herzlichen Dank.

MC3330
03-04-2009, 11:11
Hallo zusammen,
mir ist noch ein letztes Problem aufgefallen.

Manchmal existieren Tag, an denen für einen Parameter keine Wert vorliegt. Das sieht dann so aus.


07 06 20 21 00
D R 34.46 29.93 32.86 40.93 50.57 67.48 82.48 88.01 103.3 108.5 113.0257.46284.39 184.1 159.0
07 06 21 10 00
07 06 21 10 30
07 06 21 11 00
07 06 21 11 30
07 06 21 12 00
07 06 21 09 00
07 06 21 09 30
07 06 21 10 00
07 06 23 07 00
D R 99.55221.51214.74335.71203.96332.40 19.63306.53 189.1 182.9 174.5 145.3 132.6345.79359.00
07 06 23 07 30
D R 180.2355.01254.05 166.9307.77328.01212.07325.39 169.2 141.9320.26 41.88339.97293.37359.00
07 06 23 08 30
07 06 23 09 00
07 06 23 09 30
07 06 23 10 00
07 06 23 10 30
07 06 23 11 00
07 06 23 11 30
07 06 25 18 00
D R 199.8203.32280.28 48.93 4.264 7.668 144.8 168.8 162.5 109.3 6.742 152.8 167.7 45.13 6.003
07 06 28 12 30
D R 18.00268.07272.80265.62260.17270.94261.43210.53 3.977348.91 65.77 180.7 66.21243.38 58.31
07 06 28 13 00
D R 18.00256.00239.76257.04263.73260.40265.37256.78245 .86 194.6269.56 45.33217.10 34.99 162.1
07 06 28 13 30

Ist es irgendwie möglich, dass wenn mehrere Datumsstempel hintereinander kommen, alle bis auf den untersten zu löschen?

jan61
03-04-2009, 19:48
Moin,

das kannst Du elegant mit einem kleinen awk lösen:

awk ' /^[0-9]/ {ln=$0;} /^D/ {printf "%s\n%s\n",ln,$0;} ' dataawk merkt sich jede Zeile, die mit einer Ziffer beginnt, in der Variablen ln. Wenn eine Zeile mit einem "D" beginnt, dann gibt er die letzte gespeicherte Zeile und die aktuelle Zeile aus.

Jan

EDIT: Weils so schön und kurz ist, hier noch eine Variante mit sed ;-)

sed -n '/^[0-9]/h;/^D/{x;p;x;p}' dataDas bedarf, glaube ich, einer kleinen Erklärung:
Wenn eine Zeile mit einer Ziffer beginnt, dann kopiert sed diese Zeile (den "pattern space") in einen separaten Puffer ("hold space"). Beginnt eine Zeile mit einem "D", dann werden nacheinander folgende Aktionen ausgeführt:
- der Inhalt des pattern space (die aktuelle Zeile) und des hold space (die letzte Zeile, die mit einer Ziffer begann) werden ausgetauscht
- der Inhalt des pattern space (ist jetzt die letzte mit Ziffer beginnende Zeile) wird ausgegeben
- jetzt werden die beiden Puffer wieder ausgetauscht
- im pattern space steht wieder die aktuelle Zeile, diese wird jetzt auch ausgegeben

MC3330
04-04-2009, 08:17
Hi Jan,

danke, da hätte ich echt mal selber drauf kommen können.
Ich habe es jetzt so gemacht


NF==5 {ln=$0;}
NF==37 {print ln,$0}


Noch eine kleine Verständnissfrage
Was ist der unterschied zwsichen

{print ln,$0}
und

{printf "%s\n%s\n",ln,$0;}

?

jan61
04-04-2009, 16:20
Moin,





NF==5 {ln=$0;}
NF==37 {print ln,$0}


Das ist aber gefährlich, wenn ich Dein ursprüngliches Problem richtig verstanden habe. Abhängig davon, wie oft pro Zeile Felder zusammengekleistert werden, kann doch die Feldanzahl variieren, oder?


Noch eine kleine Verständnissfrage
Was ist der unterschied zwsichen

{print ln,$0}und

{printf "%s\n%s\n",ln,$0;}?

Die erste Variante schreibt die beiden Zeilen als eine Zeile raus, die printf-Variante als 2 Zeilen (es wird ein Zeilenumbruch zwischengesetzt).

Jan

P.S.: Ich würde mir auch mal die sed-Variante anschauen. Mit ein paar Änderungen könntest Du dann nämlich die gesamte Verarbeitung in ein einziges sed-Script packen und völlig ohne Pipes o. ä. auskommen.

MC3330
04-04-2009, 17:48
Moin,



Das ist aber gefährlich, wenn ich Dein ursprüngliches Problem richtig verstanden habe. Abhängig davon, wie oft pro Zeile Felder zusammengekleistert werden, kann doch die Feldanzahl variieren, oder?


Ne das passt schon, ich habe es vorher schon so bearbeitet, das die Datenzeile immer genau 37 und die Datumszeile immer 5 Felder hat. Ob er den Zeilenumbruch mitnimmt ist für mich auch egal, weil ich es eh noch mit IDL weiterverarbeite.

Das mit dem Sed lasse ich jetzt. Ich habe jetzt zwei Awk und ein Sed-Skript, die ich über meine Daten laufen lasse, um das Ergebniss zu bekommen, dass ich brauche. Das ist mir kompakt genug.