Practice of Programming

プログラム とか Linuxとかの話題

Text::Parts てのを書いた

githubにおいてます。

テキストファイルを複数のパーツに分けるものです(実際に分割するわけではなく、分けた部分を読むためのオブジェクトを返します)。
各パーツは、行頭から始まって行末で終わる感じになります。行の途中で分けられるということはありません。
とても大きいCSVファイルとかを分割する用途とかに使います。既にありそうなもんだけど、無さそうだったんで作りました。

  my $s = Text::Parts->new(file => "file.txt");
  my @parts = $s->split(num => 4); # num => 割りたい数。size => 割りたいバイト数 も可
  foreach my $part (@parts) {
      my $l = $part->getline; # or <$part>
      # ...
  }

て感じで使います。


$part はそのパートのファイルの開始と終了のポジションを持っており、オブジェクト作成時にファイルをopen & seekして、後は、<$fh>しながら、そのパートの最後の位置に達したら終了という感じの簡単なものです。


実際ありそうな、CSVファイルを分割する場合は、次のようにします。

  my $s = Text::Parts->new(file => "file.txt", , eol => "\r\n", parser => Text::CSV_XS->new({binary => 1, eol => "\r\n"}));
  my @parts = $s->split(num => 4);
  foreach my $part (@parts) {
      my $columns = $part->getline_parser;
      # ...
  }

$columns は、Text::CSV_XS のgetline の戻り値なので、パース後の配列リファレンスとなります。
Text::CSV_PP の場合、どうも壊れたCSVファイルを渡す(aaa", みたいなクォートが不一致なカラムがある)と、ファイルの終端まで読んでしまうので、check_line_start => 1 というオプションを渡してください。このオプションを付けると、行頭まで戻ってから、getline に渡します。

ただ、CSVで改行を含むカラムとかだと、壊れたフォーマットになる可能性はあるので、PP版だとうまく行きません。まぁ、XS版でうまくいってるのも、たまたまっていう話かも。


parser には、ファイルハンドルを受け取るパーサなら何でも渡せます。例えば、log のパーサとかでも良いかもしれない。
parserに渡したオブジェクトで使うメソッドは getline がデフォルトになってます。変更する場合は、parser_method オプションでメソッド名を渡してください。


なお、厳密に何行ずつに分けるのではなくて、ファイルサイズを割りたい数で割った分で分けていきます。
ただ、1行だけ非常に長いといった場合はありますので、4つに分ける場合は、

最初のパートのサイズ -- ファイルサイズ / 4 で試す
次のパートのサイズ -- 残りサイズ / 3 で試す
次のパートのサイズ -- 残りサイズ / 2 で試す
...

のようにして調整して、最初に渡された分割数になるようにしています(サイズで渡した場合も、ファイルサイズから分割数を計算しているので、同じです)。