11

次のようなサブルーチンを作成しています。

(1)CSVファイルを解析します。

(2)そして、そのファイルのすべての行に予想される列数があるかどうかを確認します。列数が無効な場合は鳴きます。

行数が数千から数百万の範囲にある場合、それを行うための最も効率的な方法は何だと思いますか?

現在、これらの実装を試しています。

(1)基本的なファイルパーサー

open my $in_fh, '<', $file or 
    croak "Cannot open '$file': $OS_ERROR";                                                            
                                                                                                      
my $row_no = 0;                                                                                           
while ( my $row = <$in_fh> ) {                                                                            
    my @values = split (q{,}, $row);                                                                      
    ++$row_no;                                                                                            
    if ( scalar @values < $min_cols_no ) {                                                                
        croak "Invalid file format. File '$file' does not have '$min_cols_no' columns in line '$row_no'.";
    }                                                                                                     
}                                                                                                         
                                                                                                      
close $in_fh                                                                                              
    or croak "Cannot close '$file': $OS_ERROR";                                                           

(2)Text :: CSV_XSの使用(bind_columnsおよびcsv-> getline)

my $csv = Text::CSV_XS->new () or                                                                         
   croak "Cannot use CSV: " . Text::CSV_XS->error_diag();                                                 
open my $in_fh, '<', $file or                                                                             
   croak "Cannot open '$file': $OS_ERROR";                                                                
                                                                                                          
 my $row_no = 1;                                                                                          
 my @cols = @{$csv->getline($in_fh)};                                                                     
 my $row = {};                                                                                            
 $csv->bind_columns (\@{$row}{@cols});                                                                    
 while ($csv->getline ($in_fh)) {                                                                         
    ++$row_no;                                                                                            
    if ( scalar keys %$row < $min_cols_no ) {                                                             
        croak "Invalid file format. File '$file' does not have '$min_cols_no' columns in line '$row_no'.";
    }                                                                                                     
}                                                                                                         
                                                                                                          
$csv->eof or $csv->error_diag();                                                                          
close $in_fh or
    croak "Cannot close '$file': $OS_ERROR";                                                           

(3)Text :: CSV_XSの使用(csv->解析)

my $csv = Text::CSV_XS->new() or                                                                         
   croak "Cannot use CSV: " . Text::CSV_XS->error_diag();                                                
 open my $in_fh, '<', $file or                                                                           
   croak "Cannot open '$file': $OS_ERROR";                                                               
                                                                                                         
 my $row_no = 0;                                                                                         
 while ( <$in_fh> ) {                                                                                    
     $csv->parse($_);                                                                                    
     ++$row_no;                                                                                          
     if ( scalar $csv->fields < $min_cols_no ) {                                                         
       croak "Invalid file format. File '$file' does not have '$min_cols_no' columns in line '$row_no'.";
     }                                                                                                   
}                                                                                                        
                                                                                                         
$csv->eof or $csv->error_diag();                                                                         
close $in_fh or 
    croak "Cannot close '$file': $OS_ERROR";                                                          

(4)Parse::CSVの使用

use Parse::CSV;                                                                                           
my $simple = Parse::CSV->new(                                                                             
    file => $file                                                                                         
);                                                                                                        
                                                                                                          
my $row_no = 0;                                                                                           
while ( my $array_ref = $simple->fetch ) {                                                                
    ++$row_no;                                                                                            
    if ( scalar @$array_ref < $min_cols_no ) {                                                            
        croak "Invalid file format. File '$file' does not have '$min_cols_no' columns in line '$row_no'.";
    }                                                                                                     
}                                                                                                         

ベンチマークモジュールを使用してベンチマークを行いました。

use Benchmark qw(timeit timestr timediff :hireswallclock);

そして、これらは私が得た数(秒単位)です:

1,000行のファイル:

実装1:0.0016

実装2:0.0025

実装3:0.0050

実装4:0.0097

10,000行のファイル:

実装1:0.0204

実装2:0.0244

実装3:0.0523

実装4:0.1050

1,500,000行のファイル:

実装1:1.8697

実装2:3.1913

実装3:7.8475

実装4:15.6274

これらの数値を考慮すると、単純なパーサーが最速であると結論付けますが、さまざまなソースから読み取ったものから、Text::CSV_XSが最速であるはずです。

誰かがこれについて私に教えてくれますか?モジュールの使用方法に問題がありますか?あなたの助けをどうもありがとう!

4

4 に答える 4

19

CSVファイルがあります

header1,header2,header3
value1,value2,value3

そしてCSVファイルがあります。

header1,"This, as they say, is header2","And header3
even contains a newline!"
value1,"value2, 2nd in a series of 3 values",value3

Text::CSVそしてその同類は、第二の種類に対処するために入念に開発され、テストされてきました。入力が単純​​なCSV仕様に準拠しており、常に準拠していると確信している場合は、パフォーマンスを上回るパーサーを構築できる可能性が非常に高くなりますText::CSV

于 2012-12-17T15:51:53.590 に答える
10

Text::CSV_XSお使いのバージョンは、単純なパーサーバージョンよりも多くのことを行うことに注意してください。行を分割してメモリに入れ、hashrefがフィールドを指すようにします。

また、エスケープされた区切り文字を許可するなど、内部に他のロジックがある場合もあります(使用していないため、わかりません)。その上、モジュールを使用するときは常に少量のオーバーヘッドがあります。関数呼び出し、パラメーターの受け渡し、そしておそらくあなたのケースには実際には当てはまらないジェネリックコード(あなたがしていないことのエラーチェックなど)気にしないでください)。

通常、モジュールを使用する利点はコストを大幅に上回ります。より多くの機能、より信頼性の高いコードなどを入手できます。しかし、それは小さくて非常に単純なタスクでは当てはまらない可能性があります。列の数を確認するだけでよい場合は、モジュールを使用するのはやり過ぎかもしれません。列の数を数えるだけで、分割することをまったく気にせずに、独自の実装をさらに高速化できます。

/(?:,[^,]*){$min_cols_no-1}/ or croak "Did not find minimum number of columns";

この検証手順に加えて実際の処理を行う場合は、モジュールを使用するとおそらく有益です。

于 2012-12-17T15:51:51.263 に答える
1

すべてのCSV解析モジュールは同じことを行います。基本的なサブで行ったのと同じように、ファイルを開いて何らかの方法でCSVを解析します。内部的には必要以上のことを行うため、オーバーヘッドがはるかに大きくなります(適切なCSV形式を確認し、オブジェクト構造を渡すなど)。そのため、さまざまな程度で、基本的なアプローチよりも遅くなります。

あなたは自分でアプローチをベンチマークしました。結果は明らかではありませんか?CSVモジュールの拡張機能が必要ない場合は、基本的な方法でCSVファイルを解析します。

(モジュールの使用法を改善することでそれらをスピードアップできるかどうかはわかりません)

于 2012-12-17T15:40:46.857 に答える
0

楽しみのために、私はこれについて正規表現をテストしました...そしてそれは機能します!;)RAMが十分にある場合は、ファイル全体を一度に読み取って、次に正規表現を使用できます。

my $blob = 'a;s;d
q;w;e
r;t;y
u;i;o
p;z;x
c;;b
n;m;f
g;h;j
k;l;';

say $blob =~ /^ ([^;]*;){2}[^;]* (\n (([^;]*;){2}[^;]*)+ \n ([^;]*;){2}[^;]*)? $/x ? 'ok' : 'bu';

ただし、これには区切り文字のエスケープ、引用符などは含まれません。指定された数の区切り文字をテストするだけです:)

于 2012-12-22T08:26:38.667 に答える