2014-02-12 5 views
6

Ich habe eine Multi-GB-Datei in Perl zu verarbeiten. Das zeilenweise Lesen der Datei dauert einige Minuten. das Lesen in einen Skalar über File :: Slurp dauert ein paar Sekunden. Gut. Nun, was ist der effizienteste Weg, um jede "Linie" des Skalars zu verarbeiten? Ich stelle mir vor, dass ich vermeiden sollte, den Skalar zu modifizieren, z. Entfernen Sie jede nachfolgende Zeile, während ich sie verarbeite, um eine Neuzuordnung des Skalars zu vermeiden.Datei :: Slurp in einen Multi-GB-Skalar - wie effizient zu teilen?

Ich habe versucht, diese:

use File::Slurp; 
my $file_ref = read_file('/tmp/tom_timings/tom_timings_15998', scalar_ref => 1 ) ; 

for my $line (split /\n/, $$file_ref) { 
    # process line 
} 

Und es ist Unter Minute: ausreichend, aber nicht so toll. Gibt es einen schnelleren Weg, dies zu tun? (Ich habe mehr Gedächtnis als Gott.)

+1

'read_file' können Sie auch auf ein Array lesen:' meine @lines = READ_FILE ('Dateiname'); 'Natürlich, werden Sie noch müssen das gesamte Array durchlaufen, um jede Zeile zu verarbeiten, also ändert es nicht viel. – ThisSuitIsBlackNot

+0

@ThisSuitIsBlackNot - Ich habe das versucht; es dauert lange. – Chap

+1

Der Grund, warum es langsam ist, ist, dass es die Datei durchsuchen muss, um nach neuen Zeilen zu suchen. Wenn es sich um feste Breitenlinien handelt, können Sie in Bytes nach der Datei suchen, die schneller sein sollte. Wenn sie Linien mit variabler Länge sind, gibt es keinen wirklichen Weg um sie herum. – Oesor

Antwort

5

split sollte sehr schnell sein, es sei denn Sie fangen an zu tauschen. Der einzige Weg, den ich sehen kann, um es zu beschleunigen, besteht darin, eine XS-Funktion zu schreiben, die nach LF sucht, anstatt eine Regex zu verwenden.

Als Nebenwirkung, könnten Sie durch die Umstellung auf

while ($$file_ref =~ /\G([^\n]*\n|[^\n]+)/g) { 
    my $line = $1; 
    # process line 
} 

Said XS Funktion viel Speicher speichern. Verschieben Sie die newSVpvn_flags Zeile nach der if Anweisung, wenn Sie nicht chomp möchten.

SV* next_line(SV* buf_sv) { 
    STRLEN buf_len; 
    const char* buf = SvPV_force(buf_sv, buf_len); 
    char* next_line_ptr; 
    char* buf_end; 
    SV* rv; 

    if (!buf_len) 
     return &PL_sv_undef; 

    next_line_ptr = buf; 
    buf_end = buf + buf_len; 
    while (next_line_ptr != buf_end && *next_line_ptr != '\n') 
     ++next_line_ptr; 

    rv = newSVpvn_flags(buf, next_line_ptr-buf, SvUTF8(buf_sv) ? SVf_UTF8 : 0); 

    if (next_line_ptr != buf_end) 
     ++next_line_ptr; 

    sv_chop(buf_sv, next_line_ptr); 
    return rv; /* Typemap will mortalize */ 
} 

bedeutet es, zu testen:

use strict; 
use warnings; 

use Inline C => <<'__EOC__'; 

SV* next_line(SV* buf_sv) { 
    ... 
} 

__EOC__ 

my $s = <<'__EOI__'; 
foo 
bar 
baz 
__EOI__ 

while (defined($_ = next_line($s))) { 
    print "<$_>\n"; 
} 
+0

Ich habe meine Antwort hinzugefügt. – ikegami

Verwandte Themen