Perl
cpan

Perlでファイルを丸呑みする3つの方法

ファイル全体を読み込んで、1つの変数に文字列として代入したい、というケースはよくあると思います。
本記事ではPerlでそれを実現する3つの方法とメリットとデメリットを紹介します。

最初に3つの方法とそれぞれのメリット、デメリットをまとめておきます。

メリット デメリット
ダイヤモンド演算子(<>)で読み込んだ結果をjoinで繋げる 誰でも理解できる 遅い。メモリを食う
特殊変数$/を空にする ほどほどに早い 可読性が低め
モジュールを使う 早い。可読性もそれなり モジュールをインストールする必要がある。openプラグマがきかないので、明示的に文字コードを指定する必要がある。

以下、utf8のファイルを読み込む前提で話を進めます。1

ダイヤモンド演算子(<>)で読み込んだ結果をjoinで繋げる

一番素朴な方法で、以下の2種類の書き方が代表的ではないでしょうか。

use open qw/:utf8/;

open(my $F, "<:utf8", 'filename') or die;
my $text;

while(my $line = $F)
{
    $text .= $line;
}

print $text;
use open qw/:utf8/;

open(my $F, "<:utf8", 'filename') or die;
my $text = join('',<$F>);

print $text;

これらの書き方の欠点ですが、読み込みながら改行(正確には後述のセパレータ)がくるかを確認すること、改行ごとに読み込みがとまるので遅いです。
さらに下の書き方の場合は、一度1行ずつのリストを作成して、それをjoinするためメモリの使用量が大きくなるという問題もあります。

メルットは誰でも理解できる、ぐらいなので普段は使わないですね。

特殊変数$/を空にする

特殊変数$/はセパレータと呼ばれていて、ダイヤモンド演算子ではこのセパレータが出てくるまでを1単位として読み込みます。
通常はこの$/に改行文字が入っているため、1行ごとの読み込みになるわけです。

この$/undefにすることで入力を確認することがなく、また止まらないので高速に読み込みができるようになります。

具体的には下記のような書き方です。

use open qw/:utf8/;

open(my $F, "<:utf8", 'filename') or die;
my $text = do { local $/; <$F> };
use open qw/:utf8/;

open(my $F, "<:utf8", 'filename') or die;
my $text;

{ 
  local $/;
  $text = <$F>;
}

2つとも実質同じです。
ポイントは閉じたスコープでlocal$/を宣言している(そしてundefが入っている)ことです。
$/はグローバル変数なので、undefにしてしまうと以降全ての箇所でファイルを丸呑みしてしまいます。
一時的に別の変数に保存して戻す手もありますが、localを使う方がスマートでしょう。

Perl知らない人には読みづらいと思いますが、イディオムとして覚えてしまっていい範囲だと思います。
どうしてもという場合はコメントでフォローしておきましょう。

欠点という程でもないですが、次の紹介するモジュールを使用するよりは遅いようです。
これはPerlのファイルIOの問題で、以下のモジュールはシステムレベルの読み込みをすることで高速化をしているようです。

モジュールを使う

Perl的にはこれが一番素直だと思います。

現在、CPANではFile::SlurpPerl6::Slurpという2つの丸呑みモジュールがあります。

前者はsysreadをラップして高速にファイルを丸呑みするためのモジュールです。
後者はPerl6の名前から分かるように、Perl6に組込み関数として導入予定のslurp関数を使えるようにするモジュールです。内部実装を眺めた限りでは、Fle::Slurpの方が速そうです。オプションなどのインターフェースがPerl6と同じになっているというのがメリットだと思います。

それぞれ以下のような書き方で動きます。

use open qw/:utf8/;
use File::Slurp;

my $text = read_file('filename', binmode => ':utf8');
use open qw/:utf8/;
use Perl6::Slurp;

my $text = slurp('<:utf8',  'filename' );

注意点は、openプラグマを書いてもSlurpに対してはきかないので、明示的に文字コードを指定しないといけない点です。
また、他にも文字コードを指定する方法や色々なオプションがありますが、それはCPANでドキュメントを見て下さい。

まとめ

Perlでファイルを丸呑みする3つの手法を紹介しました。3つと銘打って紹介しましたが、Perlらしく他の手法も沢山あります。TMTOWTDI(There's More Than One Way To Do It.)ですね。
個人的にはCPANモジュールを使える環境ならモジュールを使うのがベストだと思います。


  1. Perlに限りませんが、英語の解説とかそれを和訳したものだと、文字コードの扱い適当なことが多く、個人的にはハマりポイントだと思っています。