Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

ファイル全体を読み込んで、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に限りませんが、英語の解説とかそれを和訳したものだと、文字コードの扱い適当なことが多く、個人的にはハマりポイントだと思っています。 

mhangyo
weathernews
ウェザーニューズは、気象リスクを軽減するための気象情報サービスを行なっています。そのための気象予測やその情報活用に関するサービス開発や研究を行っています。
https://weathernews.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした