Zeilenübreifend Pattern
Cable
- perl
Hi,
ich hab folgende Datei.
Startzeit 12:00 Uhr
bla ID1
blabla
blabla
blablaID2
blabla ID1
blabla
blablabla
blablabla
blablabla ID3
blablabla ID1
blablabla
blablabla
blablabla ID2 bla bla
Stopzeit 12:45
Startzeit 13:00
blablabla ID1
blablabla
blablabla
blablabla ID10 bla bla
Stopzeit 13:01
Startzeit 13:04 Uhr
bla ID1
blabla
blablaID2
Stopzeit 13:06
Ich möchte jetzt nur alle Zeilen die zwischen ID 1 und ID 2 sind (inkl. die ID1 und ID2-Zeile) greppen. Ausserdem möchte ich dann ebenfalls die Start und Stopzeitzeile haben. Alles andere sollte raus.
Hier mal das Ergebnis, was ich gern hätte von dem Beispiel oben.
Startzeit 12:00 Uhr
bla ID1
blabla
blabla
blablaID2
blablabla ID1
blablabla
blablabla
blablabla ID2
Stopzeit 12:45
Startzeit 13:04 Uhr
bla ID1
blabla
blablaID2
Stopzeit 13:06
Hat mir da jemand mal nen Tipp, wie man das am einfachsten machen kann?
Danke.
Cable
Ich nehme an, die Informationen sind in einer log-Datei gespeichert?
Lies die Datei Zeilenweise aus und sieh nach in welcher Zeile die IDs untergebracht sind. Am besten die Datei zeilenweise in ein Array lesen. Arrayschlüssel ist dann die Zeilennummer. Dann "beschneide" deinen Speicherpuffer um die Zeilen, die kleiner sind als ID1-1 bzw größer als ID2+1 /wegen den Zeilen mit Start- und Stoppzeit)
Leichter wäre es allerdings wenn du nach Start- und Stoppzeit ausliest. Dann kannst du es in einem Schwung erledigen.
gudn tach!
Ich möchte jetzt nur alle Zeilen die zwischen ID 1 und ID 2 sind (inkl. die ID1 und ID2-Zeile) greppen. Ausserdem möchte ich dann ebenfalls die Start und Stopzeitzeile haben. Alles andere sollte raus.
also auch die zeilen, die von ID1 und IDX (X!=2) begrenzt werden.
eine moeglichkeit hat Rafael bereits genannt, also das zeilenweise abklappern wie es auch ein mensch tun wuerde.
da du in der ueberschrift was von "pattern" schriebst, moechte ich kurz noch eine regexp-moeglichkeit angeben. sie erfordert, dass die datei in einem string vorliegt.
als erstes wird der string bei den start-/stoppzeiten gesplittet:
my @arr = split(/^((?:Start|Stop)zeit .*)$/m, $str);
anschliessend wird jeder zeitblock seperat abgearbeitet
~~~perl
my $i;
for($i=@arr-2; $i>0; $i-=4){
$arr[$i]=~s/.*?(?=ID1\b|$)((?:ID1\b(?:(?!ID(?:[13-9]|2\d)).)*ID2\b)?)/$1/sg;
}
print join "\n", @arr;
nicht leicht zu verstehen ist der regulaere ausdruck
/.\*?(?=ID1\b|$)((?:ID1\b(?:(?!ID(?:[13-9]|2\d)).)\*ID2\b)?)/$1/sg;
ich werde ihn nicht komplett erklaeren, das wuerde vermutlich mehrere stunden dauern. also setze ich [grundwissen](http://perldoc.perl.org/perlre.html) voraus. falls du tatsaechlich den regulaeren ausdruck benutzen wirst, solltest du ihn genau verstanden haben, weil er einiges voraussetzt, von dem ich nicht weiss, ob es deine datei auch wirklich immer erfuellt.
.\*? matcht etwaige zu entfernende zeichen am anfang oder am ende des kompletten blockes oder zwischen gueltigen ID1...ID2-abschnitten; je nachdem an welcher stelle der regexp-cursor gerade ist; beachte den g-modifier.
.\*? matcht also z.b. auch einen ID1...ID3-abschnitt.
der term danach /(?=ID1\b|$)/ ist semantisch eigentlich ueberfluessig, es wuerde auch ohne ihn funzen. aber durch ihn wird der regulaere ausdruck beschleunigt, weil insg. weniger leere ersetzungen erfolgen, da der term .\*? damit nicht ganz so genuegsam frisst.
der grosse ausdruck
((?:ID1\b(?:(?!ID(?:[13-9]|2\d)).)\*ID2\b)?)
matcht alle gueltigen abschnitte, d.h. diejenigen, die von /ID1/ begonnen und von /ID2/ beendet werden, wobei dazwischen kein weiteres mal /ID\d/ stehen darf.
die aeusserste klammer dient nur dazu den kram dazwischen in $1 zwischenzuspeichern. $1 ist also leer, falls kein gueltiger abschnitt gefunden wird.
wenn die IDs nur einstellig sein duerften, koennten die wortenden \b wegfallen und der so entstehende term
((?:ID1(?:(?!ID[13-9]).)\*ID2)?)
waere besser verstaendlich.
zu dieser konstruktion
(?:(?!foo).)\*
vielleicht noch zwei saetze: es werden damit alle aufeinanderfolgenden zeichen gematcht, die nicht den string "foo" enthalten. auch wenn "foo" danach kaeme, wuerde von diesem term nicht mal das "f" gematcht.
was noch fehlt ist, dass die leeren bloecke eliminiert werden. aber das bekommst du wohl auch selbst hin. und ich ging davon aus, dass alle abschnitte (auch die zu loeschenden) immer mit ID1 eingeleitet werden.
prost
seth