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

Wolfgang Weisselberg pl3rofb02 at sneakemail.com
Tue Apr 27 15:28:39 CDT 2004


cem.sakaryali at easi.de wrote 115 lines:

> ein ehemaliger Kollege hat mich gefragt, ob sein Code 
> (lese 100MB file und mache =~ s///)
> optimiert werden koenne.

Laut deinem Script moechtest du kein normales Substitute,
ihr wollt in der ersten Zeile, in der ein Match vorkommt,
alle Matches substituten, aber in keiner Folgezeile, richtig?


Euer beider Code laesst 2 IMHO wichtige Optimierungen weg:
1. next unless $matches;        # no more matches?
2. print($i), next unless m/$matches/; # and using qr//

Begruendung: 
    - Du entfernst die Substitiutions.  Wenn's keine mehr
      gibt, lohnt das Testen nicht mehr, da muss man keine
      leere Schleife anfahren.
    - Matches sind ein seltenes Ereignis.  Nicht-Matches sind
      haeufig.  Also sollte man auf Nicht-Matches optimieren.

Annahmen:
- Zeilen-orientierte Datei (sonst sysread nehmen, und einen
  Mindest-Overlap von >= length($longest_s).)
- Wenige Substitutes (wenige hundert) (sonst koennte bei
  genuegend Hauptspeicher ein slurpen und 'study' besser
  sein).
- keine grosse Zahl an Matches mit substantiell gleichen
  Anfaengen (i.e. nicht Muellermeier1, Muellermeier2,
  Muellermeier3 ....), sonst sollte man gen_matchstrings
  unbedingt optimieren
- Getestet sollte es mit echten Daten (also wirklich 100 MB,
  mit wirklich der Haeufigkeit von Matches etc.).

Ach so, der Code sollte ungefaehr das richtige tun, aber ich
uebernehme keine Garantie.

Und das open will ein 'or die $!'. :-)

############################################################

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

my $file = "x";

replace_and_print( $file, %ERSETZHALTER );

############################################################

sub replace_and_print {
    my ( $in_file, %replaces ) = @_;
    open IN, $in_file or die "Cannot open $in_file: $!";
    my $matches = gen_matchstrings( \%replaces );

    while (<IN>) {
        print, next unless $matches and m/$matches/;
        print substitute( $_, \%replaces );
        $matches = gen_matchstrings( \%replaces );
    }
}

############################################################

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

############################################################

sub substitute {
    ( $_, my $replaces_href ) = @_;
    foreach my $from ( keys %$replaces_href ) {
        next unless /$from/;
        s/$from/$$replaces_href{$from}/g;
        delete $$replaces_href{$from};
    }
    return $_;
}

############################################################

Neben Perl wuerde ich auch sed o.ae. probieren.

-Wolfgang



More information about the Cologne-pm mailing list