ルモーリン
ホーム 更新 Perl Sample サービス 雑談 鉄ゲタ Linux リンク 連絡先

Perlサンプル5 複数のファイルを読み込む

2019-08-25

内容

起動時の引数に複数のファイル名を指定して、全部読み込みます。

コード

#!/usr/bin/env perl

use v5.26;
use utf8;
use strict;
use warnings;

use Encode::Argv;
use Encode::Locale;

use open IO => ":utf8 :crlf";
binmode STDIN, ":encoding(console_in)";
binmode STDOUT, ":encoding(console_out)";
binmode STDERR, ":encoding(console_out)";

$| = 1;

# ファイル名の文字コードをファイルシステムの文字コードにエンコード
$_ = Encode::encode locale_fs => $_ for @ARGV;

my $line = 0;
while(<<>>) {
	# 各ファイルの先頭にファイル名
	say "-- @{[Encode::decode locale_fs => $ARGV]} --" if 1 == $.;

	chomp;
	$line++;
	say "行 $. / $line, '$_'";
} continue {
	# ファイルの最後で行番号をリセット
	$. = 0 if eof;
}

ファイルを用意

2つのテキストファイルを作ります。 文字コードはUTF-8、改行コードはどちらでも読めるコードにしてありますので、どちらでも構いません。

たこルカは俺の嫁
家には4人いますΩ
ルカ姐さんも俺の嫁
家には
小さいルカ姐さんが3人
大きいルカ姐さんが4人
います

実行結果

引数をperl perlsample_005.pl perlsample_005_たこルカ.txt perlsample_005_ルカ姐さん.txtとした場合の結果です。

-- perlsample_005_たこルカ.txt --
行 1 / 1, 'たこルカは俺の嫁'
行 2 / 2, '家には4人いますΩ'
-- perlsample_005_ルカ姐さん.txt --
行 1 / 3, 'ルカ姐さんも俺の嫁'
行 2 / 4, '家には'
行 3 / 5, '小さいルカ姐さんが3人'
行 4 / 6, '大きいルカ姐さんが4人'
行 5 / 7, 'います'

解説

use open IO => ":utf8 :crlf"

Perlが自動的にopenするので、:crlfをデフォルトに追加します。

while(<<>>)

指定されたファイルを次々と開き、1行ずつのループを回します。 この書き方は、ファイル境界がありません。 ただループを回すと全ファイルを連結したかのように読み込みますので、ファイル境界を把握したい場合は工夫が要ります。 似た書き方のwhile(<>)は2引数のopenですから、シェル文字を含むファイル名があれば誤動作するのでお勧めしません。
「I/O 演算子」
「空ファイルハンドルは 2 引数の "open" in perlfunc を使った特別な文字列」
「これを perl dangerous.pl 'rm -rfv *|' として呼び出すと、実際には パイプを開き、rm コマンドを実行して、 rm の出力をパイプから読みます。」
perlop - Perl の演算子と優先順位 - perldoc.jp

$.

1から始まる行番号が入っていて1行読むと1増えます(そりゃそうだ)。 複数のファイルを読んだ際にリセットされないので全ファイルの通し番号になります。 そこで、このサンプルではファイル境界を判定してリセットします。
「最後にアクセスされたファイルハンドルの現在の行番号。」
「$. はファイルハンドルがクローズされるとリセットされますが、 オープンしているファイルハンドルが close() されることなく再オープンされた 場合にはリセット されません。 」
Perlの組み込み変数 $. の翻訳 - perldoc.jp

continue

「これは C における for ループの 3 番目の部分と同様です。 従って、これは next 文 (これは C の continue 文と似ています) を使って ループが繰り返されるときでもループ変数を増やしたいときに使えます。」
Perlの組み込み関数 continue の翻訳 - perldoc.jp

eof

ファイルを最後まで読み込んだかをチェックしています。 最後の場合は行番号をリセット、次のファイルの1行目がファイル境界と判定できるようにします。
「次に FILEHANDLE 上で読み込みを行なったときに、EOF が返されるときか、 または FILEHANDLE がオープンされていないと、1 を返します。」
「引数を省略した eof は、最後に読み込みを行なった ファイルを使います。」
Perlの組み込み関数 eof の翻訳 - perldoc.jp