[Cologne-pm] PERL - schnelles suchen & ersetzen in 100MB Datei

A. Pagaltzis pagaltzis at gmx.de
Tue Apr 27 20:07:01 CDT 2004


Hallo,

vorab an Cem: danke, dass du hin und wieder für Leben auf der
Liste sorgst. :-)


* Wolfgang Weisselberg <pl3rofb02 at sneakemail.com> [2004-04-27 22:32]:
> 1. next unless $matches;        # no more matches?
> [...]
>     - Du entfernst die Substitiutions.  Wenn's keine mehr
>       gibt, lohnt das Testen nicht mehr, da muss man keine
>       leere Schleife anfahren.

Nachdem es keine Arbeit mehr gibt, bedeutet zeilenweises Lesen
überflüssige Arbeit. Man will dann in einen blockorientierten
Modus wechseln und möglichst grosse Happen auf einmal lesen und
schreiben. Geht auch ohne Sysread: man setze $/ auf eine Referenz
auf eine Zahl, die die Blockgrösse angibt. Siehe perldoc perlvar.

> 2. print($i), next unless m/$matches/; # and using qr//
> [...]
>     - Matches sind ein seltenes Ereignis.  Nicht-Matches sind
>       haeufig.  Also sollte man auf Nicht-Matches optimieren.

Man macht (fast) nie ein m//, um zu prüfen ob ein s/// nötig ist,
denn s/// muss die gleiche Arbeit wie m// nochmal leisten. Man
macht gleich das s///, denn anhand von dessn Rückgabewert sieht
man, ob was passiert ist oder nicht.

> - keine grosse Zahl an Matches mit substantiell gleichen
>   Anfaengen (i.e. nicht Muellermeier1, Muellermeier2,
>   Muellermeier3 ....), sonst sollte man gen_matchstrings
>   unbedingt optimieren

Siehe Regex::PreSuf (ja, es ist wirklich Regex:: ohne p),
Regexp::List.

>     open IN, $in_file or die "Cannot open $in_file: $!";

Bitte entweder (nicht so toll)

    open IN, "<$in_file"

oder (besser)

    open IN, '<', $in_file

Aber nicht ohne Modus schreiben -- sonst wird man irgendwann
durch seltsame Dateinamen vom magic open überrascht. Erstere Form
kommt übrigens nicht mit Dateinamen zurecht, die mit Leerzeichen
anfangen.

Siehe "Two-arg open() considered dangerous",
<http://www.perlmonks.org/index.pl?node_id=131085>

Darüber, ob man Package-Filehandles verwenden sollte, kann man
auch streiten. Ich nehme lieber grundsätzlich lexikalische
Variablen, also

    open my $fh, '<', $in_file

> sub gen_matchstrings {    # where is map for hashes??
>     my $in = shift;
>     my $string = join( '|', keys %$in );
>     return qr/$string/;    # precompile regexp
> }

Vorsicht! Du willst

    my $string = join( '|', map quotemeta, keys %$in );

Sonst spielen zufällig vorhandene Metazeichen in der Eingabe ihre
Streiche mit dir.

>     foreach my $from ( keys %$replaces_href ) {
>         next unless /$from/;
>         s/$from/$$replaces_href{$from}/g;
>         delete $$replaces_href{$from};
>     }

Ganz schön umständlich und ineffizient.

s/// muss für jedes $from das Pattern neu kompilieren.

Ausserdem wieder die gleiche Falle mit Metazeichen in der
Eingabe; es sollte C<s/\Q$from/.../> sein.

Besser geht es, wenn du den vorher kompilierten Regex mit
Klammern versiehst, dann kann er hier zum Matchen verwendet
werden, wobei $1 die Ersetzung auswählt.  Die explizite Schleife
wird mit Hilfe von /e dann ganz überflüssig.


So sieht summa summarum meine Fassung aus:

    #!/usr/bin/perl
    use strict;
    use warnings;

    sub rx_from_list { map qr/($_)/, join '|', map quotemeta, @_ }

    my %subst_for = (
        's1' => 'e1',
        's2' => 'e2',
        's3' => 'e3',
        's4' => 'e4',
    );

    my $matches = rx_from_list keys %subst_for;

    open my $fh, '<', $filename
        or die "Cannot open $filename: $!";

    while (<$fh>) {
        my %found;
        s[ $matches ][ $found{$1}++; "$subst_for{$1}"; ]xge;
        print;

        if(%found) {
            delete @subst_for{ keys %found };

            # ultrashortcircuit if no work left
            unless ( %subst_for ) {
                local $/ = \262144; # read 256kb chunks
                print while <$fh>;
                last;
            }

            $matches = rx_from_list keys %subst_for;
        };

    }

-- 
Gruss,
Aristoteles
 
"Wer nicht über sich selbst lachen kann, nimmt das Leben nicht ernst genug."



More information about the Cologne-pm mailing list