Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]13183[/thread]

Dateien durchsuchen, über mehrere Zeilen hinweg

Leser: 4


<< >> 7 Einträge, 1 Seite
Breston
 2009-02-25 18:43
#119142 #119142
User since
2009-02-25
4 Artikel
BenutzerIn
[default_avatar]
Hallo zusammen,

kurz vorweg: ich bin völliger Perl-Anfänger und habe ein Problem, bei dem ich dachte, mit ein bischen Einlesen und Anschauen von Beispielprogrammen kriege ich es hin. Mittlerweile bin ich aber verzweifelt - es klappt einfach nicht.

Die Problemstellung: ich habe eine Datei, die eine ganze Reihe von folgenden Zeilenpaaren enthält:

AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AAU1995#ARR1995#ZZZEND#

Ich möchte nun in einem ersten Schritt nach bestimmten Konstellationen suchen - alle Zeilenpaare, in denen der Wert von AHF 020 ist (also der String AHF020 vorkommt), der Wert von ADC 400 ist (String ADC400), und in der nächsten Zeile die Felder AAU und ARR auf 1995 stehen. Die Feldbezeichnungen sind also fix, die Werte variabel. Aber: AAU und ARR stehen immer in einer anderen Zeile als AHF und ADC. Und das ist das Problem, an dem meine Suchkriterien alle scheitern.

Die Trefferzeilen sollen letztendlich ausgegeben werden. Womit ich das mache, wäre eigentlich egal. Aber die Suchfunktion der regulären Ausdrücke in vi und sed erstrecken sich immer nur über eine Zeile, wie ich lernen musste. In Perl sollte das aber doch im Prinzip möglich sein, oder?

Kann mir jemand Hinweise geben, wie ich das mache? Wie gesagt, bin völliger Perl-Anfänger. Laufen soll das ganze dann einfach von der Shell-Oberfläche unter Unix.

Vielen Dank,

Breston
Linuxer
 2009-02-25 19:21
#119148 #119148
User since
2006-01-27
3890 Artikel
HausmeisterIn

user image
Hi,

unter der Annahme, dass die Zeile mit AAU und ARR immer direkt nach der Zeile mit AHF und ADC folgt, folgender Quickhack zur Veranschaulichung:

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#! /usr/bin/perl -l
use strict;
use warnings;

# Datei öffnen bitte implementieren

# Dateihandle ersetzen durch oben genutzten
while ( my $line = <DATA> ) {

    my $pos = tell DATA;      # Dateihandle ersetzen

    # Bei dieser Suchmethode ist die Reihenfolge der gesuchten Strings egal.
    
    # Suche nach festem String ohne Regex:
    #if ( index($line, 'AHF020', 0) >= 0 and index($line, 'ADC400', 0) >= 0 ) {
    # Such mit Regex
    if ( $line =~ m{AHF020} and $line =~ m{ADC400} ) {

        # naechste Zeile einlesen
        my $line2 = <DATA>;        # Dateihandle ersetzen

        if ( $line2 =~ m{AAU1995} and $line2 =~ m{ARR1995} ) {
            print $line, $line2;
        }
        else {
            # kein Treffer in der zweiten Zeile, also Pointer zuruecksetzen
            # damit die Suche nach der ersten Zeile beim n?chsten Durchlauf
            # erfolgen kann.
            seek( DATA, $pos, 0 );               # Dateihandle ersetzen
        }
    }
}


# Datei schliessen bitte implementieren

# Der folgende Teil kann wegfallen, wenn aus der Datei gelesen wird
__DATA__
AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AAU1995#ARR1995#ZZZEND#
AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AAU1995#ARR1995#ZZZEND#


Wenn die Datenzeilen immer das gleiche Format haben, und die Felder die gleiche Reihenfolge, dann könnte man auch jede Zeile splitten und die entsprechende Feldwerte direkt vergleichen.
Wie man es letztendlich macht oder machen sollte, hängt von der Genauigkeit der Aufgabendefinition ab und dem gewünschten Ziel, welches man erreichen will.

edit: s/genauigkeit aufgabendef/genauigkeit der aufgabendef/
Last edited: 2009-03-19 12:14:56 +0100 (CET)
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!
Breston
 2009-02-25 19:41
#119149 #119149
User since
2009-02-25
4 Artikel
BenutzerIn
[default_avatar]
Linuxer+2009-02-25 18:21:03--
unter der Annahme, dass die Zeile mit AAU und ARR immer direkt nach der Zeile mit AHF und ADC folgt, folgender Quickhack zur Veranschaulichung:
[...]
Wenn die Datenzeilen immer das gleiche Format haben, und die Felder die gleiche Reihenfolge, dann könnte man auch jede Zeile splitten und die entsprechende Feldwerte direkt vergleichen.


Zunächst einmal: vielen Dank für die Mühe, die Du Dir gemacht hast!

Ja, die Zeilen folgen immer direkt aufeinander. Die Reihenfolge der Felder bleibt auch wohl immer gleich, das Format aber nicht unbedingt - es können wohl noch Felder wegfallen oder eingeschoben werden.

Aber: gibt es wirklich keine elegante Möglichkeit, mit einer regexp über zwei Zeilen hinweg zu suchen? An die Möglichkeit, das ganze in zwei Schleifen mit zwei Abfragen aufzuteilen, hatte ich auch schon gedacht, aber ich war bisher fest davon ausgegangen, dass es auch eleganter gehen müsste? ZZZEND# ist übrigens in jedem Falle das eindeutige Ende des zweizeiligen Datensatzes, falls das irgendetwas nützt.
Linuxer
 2009-02-25 23:01
#119156 #119156
User since
2006-01-27
3890 Artikel
HausmeisterIn

user image
Naja, Eleganz liegt im Auge des Betrachters ;o)

Wenn ZZZEND# immer das Ende eines Datensatzes ist, dann könnte man $/ entsprechend lokalisieren, dass anstatt zeilenweise eben datensatzweise eingelesen werden kann.

Dann könnte man es so tun:

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#! /usr/bin/perl
use strict;
use warnings;

# Datei öffnen bitte implementieren

{
    # EDIT: fixed String
    local $/ = "ZZEND#\n";

    while ( my $dataset = <DATA> ) {  # ersetze DATA mit Dateihandle
        #### Entweder
        # EDIT: ohne regex und Ruecksicht auf Verteilung auf Zeilenanzahl
        if ( index($dataset, 'AHF020', 0 ) >= 0 
          && index($dataset, 'ADC400', 0 ) >= 0 
          && index($dataset, 'AAU1995', 0 ) >= 0 
          && index($dataset, 'ARR1995', 0 ) >= 0 
        ) {
            print $dataset;
        }
        # oder mit regex (dieser ignoriert die Anzahl der Zeilen auf die die 4 festen Strings verteilt sein können
        if ( $dataset =~ m/AHF020.*ADC400.*AAU1995.*ARR1995/s ) {
            print $dataset;
        }
        # EDIT: oder mit regex (dieser macht es erforderlich, dass die ersten 2 festen Strings in einer und die anderen 2 in der nächsten auftauchen
        if ( $dataset =~m/AHF020.*ADC400.*\n.*AAU1995.*ARR1995/ ) {
            print $dataset;
        }
    }
}

# Datei schliessen bitte implementieren

# Der folgende Teil kann wegfallen, wenn aus der Datei gelesen wird
__DATA__
AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AAU1995#ARR1995#ZZZEND#
AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AIEA7#AHF020#AQX100#AJK888#AFED#CLD200901#ADC400#ADL0000000000000025,00+#AEA111#
AAU1995#ARR1995#ZZZEND#


Edit: striked:
[s]Beachte aber, dass das Verhalten der beiden Lösungen (meine erste und jetzt diese) etwas unterschiedlich ist und dass die Ausgabe sich entsprechend unterscheidet![/s]
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!
Breston
 2009-02-26 12:45
#119168 #119168
User since
2009-02-25
4 Artikel
BenutzerIn
[default_avatar]
Linuxer+2009-02-25 22:01:07--
[perl]
#! /usr/bin/perl
# oder mit regex (dieser ignoriert die Anzahl der Zeilen auf die die 4 festen Strings verteilt sein können
if ( $dataset =~ m/AHF020.*ADC400.*AAU1995.*ARR1995/s ) {
print $dataset;
}
# EDIT: oder mit regex (dieser macht es erforderlich, dass die ersten 2 festen Strings in einer und die anderen 2 in der nächsten auftauchen
if ( $dataset =~m/AHF020.*ADC400.*\n.*AAU1995.*ARR1995/ ) {
print $dataset;
}


Ja genau, an die Suche mit derartigen regulären Ausdrücken hatte ich eigentlich gedacht - und dachte, das müsste doch ganz einfach sein, am besten ein Einzeiler auf der Shell-Oberfläche oder so...

Habe jetzt auch mal Dein Beispielprogramm in diesen beiden Versionen ausprobiert. Bei mir funktioniert es leider nicht - entweder er gibt die ganze Datei aus (falls ich einen Suchstring eingebe, den es in der Datei gibt) - oder er gibt gar nichts aus. Die für mich relevanten Zeilen selektiert er leider nicht. :-(

Edit: Ich vermute mal, dass er die ganze Datei jetzt als einen einzigen Datensatz behandelt, der entweder das Suchkriterium erfüllt oder nicht?!
Linuxer
 2009-02-26 14:06
#119173 #119173
User since
2006-01-27
3890 Artikel
HausmeisterIn

user image
Ohne den Code zu sehen, den Du nun einsetzt und ohne Beispieldaten zu haben, mit denen man das von Dir beobachtete Verhalten nachstellen könnte, ist es schwer, dazu eine Aussage zu treffen.

Die von mir gezeigten Beispiele funktionierten meines Erachtens wie gewünscht (jedenfalls größtenteils).
meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!
Breston
 2009-02-26 15:56
#119179 #119179
User since
2009-02-25
4 Artikel
BenutzerIn
[default_avatar]
Linuxer+2009-02-26 13:06:14--
Die von mir gezeigten Beispiele funktionierten meines Erachtens wie gewünscht (jedenfalls größtenteils).


Ja - bei mir jetzt, endlich, auch. Ich musste in der Definition des fixed Strings hinter dem ZZZEND# den Zeilenumbruch '\n' entfernen - dann ging es. Irgendwie scheint der ein Problem zu machen - keine Ahnung, warum. Ich arbeite auf einem relativ alten AIX-System - ist da irgendetwas falsch definiert oder so?

Anyway: so wie es aussieht, funktioniert erstmal alles so, wie ich es möchte.

Vielen Dank!
<< >> 7 Einträge, 1 Seite



View all threads created 2009-02-25 18:43.