=encoding euc-jp =head1 Perl Slurp概要 =head2 はじめに Perlでよくみかけるコードはテキストファイルを行単位に処理することです: while( ) { do something with $_ } このコードは複数の変数を持っています。しかしキーポイントは各ループの 繰り返しの度にファイルから1行だけ読み込むことです。これには メモリを1行に使われるだけに制限できる、(STDINを経由してパイプされた データも含めて)どんな大きさのファイルも扱うことができる、Perl初心者にも 教えやすく理解しやすいといった、いくつかの利点があります。実際のところ、 初心者は以下のような馬鹿げたことをしがちです: while( ) { push @lines, $_ ; } foreach ( @lines ) { do something with $_ } 行毎の処理は素晴らしいのですが、ファイルの読み込みを処理する唯一の方法では ありません。他のよくあるスタイルはファイル全体をスカラーや配列に読み込む ことです。これは俗に丸呑み(slurping)といわれています。現在、丸呑みの 評判はあまりよくありません。この記事はそれを社会復帰させようとしています。 ファイルの丸呑みには利点と制限があります。行毎の処理がよい場合には 単純に行うべきものではありません。一度に処理するため、メモリ上に ファイル全体を必要とするときには最もよいことです。 適切に行われれば、メモリ処理を伴う丸呑みは、行ごとに処理するよりも 速く、コードをシンプルにします。 丸呑みを見ていて一番の問題はファイルの大きさです。非常に大きなファイル やSTDINからデータの量がわからないものを丸呑みすることは、メモリ使用に 損害をもたらし、スワップ・ディスクをスラッシングさせてしまうかもしれません。 メモリ使用に悪影響を与えることなく最大サイズの入力を扱うことが分かっている 場合にのみ、STDINを丸呑みすることができます。そこで私はディスク・ファイルだけを、 それもほどほどの大きさであることを知っていて、ファイルを丸ごと処理する ちゃんとした理由がある場合にのみ丸呑みすることを主張します。 今日の、ほどほどの大きさはRAMが限られていた昔よりも大きいことに 注意してください。メガバイトを丸呑みしても、ほとんどのシステムでは 問題はないでしょう。しかし私が丸呑みしようとする、ほとんどのファイルは それよりも大分小さくなりがちです。丸呑みがうまくいく典型的なファイルは 設定ファイル、(ミニ-)言語スクリプト、いくつかのデータ(特にバイナリ)ファイル、 そして早く処理する必要がある大きさが分かっている他のファイルです。 行単位よりに丸呑みが勝利する大きなもう一つの点は速度です。Perlの IOシステムは(他の多くと同様に)遅いものです。行ごとにC<< <> >>を呼び出す ことは、行末をチェックし、EOFをチェックし、行をコピーし、内部ハンドル 構造体を加工するなどを行います。行の読み込みのために各行のたくさんの 作業が行われます。これに反して丸呑みは、もし正しければ、通常 1回だけのI/O呼び出しを伴い、余分なデータのコピーはありません。 ディスクへの書きこみでも同じことがいえます。そして私たちは それもカバーします(端末丸呑みが伝統的な読みこみ操作であっても、 ファイル全体へのI/Oを1回の操作で行うという考え方のため、 "丸呑み"という言葉を使います)。 最後にファイル全体をメモリ上に丸呑みしたときには、行単位の処理を 行うことは可能ではないか、簡単ではないデータを操作することができます。 これには(改行は無視した)グローバルな検索/置換、Cを呼び出すことで 全てのマッチを捕らえること、複雑な解析(これは多くの場合、改行を無視 しなければなりません)、*MLの処理(そこでは行の末尾は単なる空白です)、 テンプレート展開のような複雑な変形が含まれます。 =head2 グローバルな操作 丸呑みされたファイル全体に対して早く簡単に行うことができる、いくつかの 簡単なグローバルな操作を以下に示します。それらは行単位の処理によっても 行うこともできます。しかしそれはより遅くより多くのコードを必要と します。 よくある問題は、キー/値の組を持ったファイルを読み込むことです。これを 行うモジュールもあります。しかし簡単なフォーマットのためにそれらが 必要な人はいるでしょうか?単純にファイルを丸呑み、全てのキー/値のペアを 全て捕らえるために一回の解析を行ってください。 my $text = read_file( $file ) ; my %config = $test =~ /^(\w+)=(.+)$/mg ; 行の始まりにあるキー(C修飾子のために文字列のどこにあっても)、 =文字、そして行の末尾にあるテキスト(再びCがその働きをおこないます)に マッチします。実際には末尾C<$>も必要ですらありません。というのも C<.>は通常は改行にマッチしないからです。キーと値が捕らえられ、Cは C修飾子付きでリスト・コンテキストであるために、それは全ての キー/値の組を捕らえ、それらを返します。C<%config>ハッシュは、このリストが 代入され、完全に解析されたファイルをハッシュに持っていることになります。 私が働いてきたさまざまなプロジェクトでは、いくつかの簡単なテンプレートを 必要としていました。そして私は完全なモジュールを使う気にはなれませんでした。 (お願いですから、あなたの好きなテンプレート・モジュールについて熱くならない でください :-)。そこで私はテンプレート・ファイルを読みこみ、 テンプレート・ハッシュを設定し、これを1行で行ってきました: $text =~ s/<%(.+?)%>/$template{$1}/g ; これはファイル全体が丸呑みされている場合にのみ機能します。ほんの少しの 余分な作業で拡張されるテキストの固まりを扱うことができます: $text =~ s/<%(\w+)_START%>(.+)<%\1_END%>/ template($1, $2)/sge ; マーカーの間のテキストを展開するためにC