2014-02-12 26 views
6

我有一個用GB處理的多GB文件。逐行讀取文件需要幾分鐘的時間;通過File :: Slurp將它讀入標量需要幾秒鐘的時間。好。現在,處理標量的每個「行」的最有效方法是什麼?我想我應該避免修改標量,例如在我處理它時避免每一個連續的行,以避免重新分配標量。File :: Slurp成多GB標量 - 如何有效地分割?

我嘗試這樣做:

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 
} 

而且它分分鐘:充足的,但不是很大。有沒有更快的方法來做到這一點? (我比上帝有更多的記憶。)

+1

'read_file'也可以讀取到一個數組:'我@lines = READ_FILE( '文件名');'當然,你還是會必須遍歷整個數組來處理每一行,所以它不會有太大的改變。 – ThisSuitIsBlackNot

+0

@ThisSuitIsBlackNot - 我試過了;花費很長時間。 – Chap

+1

它慢的原因是它需要通過文件尋找換行符。如果它們是固定寬度線,則可以通過文件查找字節,這應該更快。如果他們是可變長度的線條,那就沒有真正的方法。 – Oesor

回答

5

split應該是非常快的,除非你開始交換。我能看到加速的唯一方法是編寫一個查找LF而不是使用正則表達式的XS函數。

順便說一句,你可以通過切換節省大量的內存來

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

說XS功能。如果您不想chomp,請在if語句後移動newSVpvn_flags行。測試它的

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 */ 
} 

方式:

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

我已經添加到我的答案。 – ikegami

相關問題