2部 Perl言語仕様

ファイルハンドルと出力

ファイルの入出力

ファイルの読み込みや書き込みをするには、まず、ファイルを開いて『ファイルハンドル』に関連付けし、そのファイルハンドルを操作するのが基本手順になります。

ファイルオープン

それでは、ファイルを開いてファイルハンドルに関連付けをして見ましょう。ファイルのオープンには、open関数を使います。書式は以下の通りです。

構文

open FILEHANDLE, [EXPR]

EXPR で指定したファイル名をファイルをオープンして、FILEHANDLE に対応させます。

openは、ファイルを開くための関数です。EXPRで指定したファイル名をファイルをオープンして、FILEHANDLEに対応させます。ファイルハンドル名には慣習的に半角大文字で名前を付けるのが決まりになっています。EXPRを省略すると、FILEHANDLEと同じ名前のスカラ変数に、ファイル名が入れられているものとして処理されます。open は、成功時には、0以外を返し、失敗時には未定義値を返します。

# 一般的な open
open FH, ">filename.txt";
# EXPRを省略
$FH = "filename.txt";
open FH;

ファイルの読み書きには >、>>、<を使います。ファイル名の前に、以下の記号を付けることにより、モードを指定できます。

ファイル入出力記号の一覧
  読み取り 書き込み 追加 新規作成 上書き
< filename × × × ×
> filename × ×
>>
filename
× ×
+<
filename
× × ×
+>
filenames
×
+>>
filename
×
command | command
を実行し、その出力をパイプ経由でファイルハンドルを渡す
| command 出力とファイルハンドルをパイプで command に渡す
- STDINをオープン
>- STDOUTをオープン
>& 式がテキストであればファイルハンドルの名前と解釈。数値であればファイル記述子と解釈。

ファイル名の先頭に <を付けるか、何も付けなかった場合には、入力用としてオープンされることになります。> と >>との動作の違いは、> はファイルサイズを0にしてから上書きするのに対し、>>
はファイルの終わりにデータを追加します。> や < の前に +を置くことで、ファイルを読み書き両用にすることもできます。

ファイルの読み込み

ファイルをオープンしたあと、読み出すには行入力演算子 <> を使います。

open(FH,"<data.dat"); # 入力モードでオープン
$line = <FH>; # 1行読み込み

全行を読み込むには、配列で受け取ります。この場合、ファイルの内容が、行単位で配列変数に代入されます。

# ファイルの1行目を読み込む
open(FH, $datafile);
@list = <FH>;
# 1行ごとに出力
foreach $data_line( @list ) {
	print $data_line;
}

while文でファイルのすべての行を読み込んでいくこともできます。

# ファイルを1行ずつ読み込み、EOFで終了
open(IN, $datafile);
while ( $line = <IN >) {
  print $line;
}

行入力演算子<>はファイルハンドルに関連付けられたファイルを行単位で読み込みます。デフォルトで特殊変数$_に読み込んだ行を代入するので、上記の文は次のように省略することができます。

# 特殊変数 $_ に読み込んだ行を代入
while ( <IN >) {
  # 特殊変数 $_ の値を出力
  print;
}

ファイルへの出力

いつも使ってる、print の第1引数にファイルハンドルを指定します。

open(OUT, ">data.dat"); # 上書きオープン
print OUT $line; # data.dat に $line の内容を出力

ファイルの末尾にデータを追加する

open 関数のファイル入出力記号に >>を指定してファイルをオープンします。それ以外はファイルの上書きオープンとまったく同じ手続きで処理できます。

open(OUT, ">>data.dat"); # 追加オープン
print OUT $line; # data.dat に $line の内容を出力

ファイルを閉じる

ファイルのクローズには、close命令を使います。FILEHANDLEには、open命令で付けた名前を指定します。ファイルをclose しなければファイル内容は更新されません。

open(FH, ">$datafile");
print FH $line;
close(FH);

例外処理

openは、ファイルが開けなかった場合に未定義値を返すので、その値をチェックすれば例外処理を行うことができます。

読み込みオープンのエラー処理付き
if( ! open FH , "$filename" ){
	# ファイルオープンエラー処理
	....
}
読み込みオープンのエラー処理(1行)付き
$filename = "./data.dat";
open( FH , "$filename" ) || die "Error: $filename $!\n";

ファイルテスト演算子

ファイルテスト演算子を利用すれば、ファイルの属性が適切にセットされていることを確認することができます。

ファイルテスト演算子の一覧
演算子 説明
-r 読み込み可能
-w 書き込み可能
-x 実行可能で
-o 実行者とファイルの所有者が同一
-R 実uid/gidで読み込み可能
-W 実uid/gidで書き込み可能
-X 実uid/gidで実行可能
-O 実uidとファイルの所有者が同一
-e ファイルが存在する
-z ファイルサイズが 0
-s ファイルサイズが 0 以外(大きさを返す)
-f ファイルは通常ファイル
-d ファイルはディレクトリ
-l ファイルはシンボリックリンク
-p ファイルは名前付きパイプ
-S ファイルはソケット
-b ファイルはブロック型の特殊ファイル
-c ファイルはキャラクタ型の特殊ファイル
-t ファイルハンドルが tty としてオープンされている
-u ファイルの setuid ビットがセットされている
-g ファイルの setgif ビットがセットされている
-k ファイルの sticky ビットがセットされている
-T ファイルがテキストファイル
-B ファイルがバイナリファイル
-M perl起動時における、ファイルの更新時刻からの日数
-A perl起動時における、ファイルの参照時刻からの日数
-C perl起動時における、ファイルの作成時刻からの日数

ファイルテスト演算子の使い方

$file = "/home/httpd/html/index.html";
# ファイルが存在するか確認
if ( -e $file ){
	....
}

パイプ

UNIX 系 OS 全般や、Windows NT といったサーバ OSには、パイプが用意されています。パイプは、プロセス間で通信するために用意された機構で、標準出力と標準入力を経由してデータを受け渡しするためのものです。これによって、一時ファイルなどを経由せずに、ダイレクトにプログラムが標準出力に出力した結果を他のプログラムの標準入力に渡すことができます。

open でパイプ処理

以下では Perl からパイプを使った例を紹介します。ファイル名の先頭に | を付けると、そのファイル名をコマンドとして解釈し、そのコマンドラインへ出力します。

sendmail を使ってメールを送信する例です。

open FH, '|/usr/sbin/sendmail -t user@foo.com' or die "Can't Open\n";
print FH 'From: user@foo.com', "\n";
print FH 'To: user@bar.com', "\n";
print FH "Subject: test mail.\n\n";
print FH "Good!.\n";
close FH;

上記とは逆に、ファイル名の最後に | を付けた場合には、同様にファイル名をコマンドと解釈し、そのコマンドの出力を読み込むことができるようになります。

# cat コマンドの出力を読み取る
open( FILE, "cat $text |");
while ( <FILE> ){
	print;
}
close( FILE );

ファイルをパイプを使ってオープンする場合は、ファイルが開けない場合でもエラーを示す未定義値は返ってきません。パイプを作成できなかったときだけ、エラーステータスが返されます。パイプオープンのエラーを捕らえるには、特殊変数 $? を利用します。ファイルハンドルがクローズされると、特殊変数 $? にリターンステータスをセーブするので、これを利用します。

open( FILE, "cat $text |") or die "Can't open pip $text";
while ( <FILE> ){
	print;
}
close( FILE )
if ( $? ){
	print "Can't open $text"; exit;
}

プロセス間の入出力

現在のプロセスの子プロセスを作り、出力を渡したいときは、open 文に引数として |- を渡します。

open( CH, "|-" );

親プロセスからの出力をその子プロセスで出力するには次のようにします。

print <>

逆に、子プロセスから親プロセスに出力を渡すこともできます。親プロセス側では open 文に引数として -| を渡します。

if( open CH, "-|" ){
	print <CH>;
	close(CH);
}

子プロセス側では print で親プロセスに出力を渡します。

print "...";

ファイル操作テクニック

ファイルハンドルを変数に格納する

ファイルハンドルを変数に格納するには、型グロブ( * )を使います。この方法が最も簡単で処理速度も速いです。

open( FILEHANDLE, "$file_name");
$fh = *FILEHANDLE;    # ファイルハンドルを変数に代入
&function(*FILEHANDLE);    # 型グロブを使ってサブルーチンに渡すこともできます

IO::FileHandle や IO::Fileモジュールを使って、ファイルハンドルをオブジェクトとして扱うこともできます。

use IO::File;    # 5.004以上
$fh = IO::File->new("< file.txt");

一時ファイルを作成する

一時ファイルを作成し、プログラムが終了したら自動的に削除されるようにするには、IO::Fileモジュールを使います。一時ファイルは、プログラムが終了するかファイルがクローズされると、自動的に削除されます。

use IO::File;
# 一時ファイルを作成
my $fh = IO::File->new_tmpfile;
# 一時ファイルに出力。ただし、プログラム終了時には自動的に削除される
print $fh "test", "\n";

ファイルをオープンしている状態で更新する

ファイルをオープンしている状態で、上書き更新するには次のようにします。

open( OLD, "< $old" );
open( NEW, "> $new" );
while ( <OLD> ) {
	...    # $_ を変更する処理
	print NEW $_;
}
close( OLD );
close( NEW );
rename( $old, "$old.orig" );
rename( $new, $old );

10行目に行を挿入する場合は、上記の while の部分を次のように変更します。

while ( <OLD> ) {
	if ($. == 20) {
		print NEW "Extra line\n";
	}
	print NEW $_;
}

10~20行目を削除するのも簡単です。

while ( <OLD> ) {
	next if 20 .. 30;
	print NEW $_;
}

ファイルを削除する

unlink() を使えば、ファイルを削除することができます。unlink()は、引数として渡されたファイル名のリストを削除します。引数を省略すると、$_によって指定されたファイルを削除します。最後に、削除に成功したファイルの個数を返します。

複数のファイルを削除
# a.txt, b.txt, c.txt ファイルを削除
unlink 'a.txt', 'b.txt', 'c.txt';

関連記事