続・はじめてのPerl 8章

今日も(ry

8.1 古い方法
ファイルハンドルはデータ型の1つ
    ライブラリや、コードの他の部分で共有するには?
        型グロブや型グロブリファレンスを使う

型グロブ
    名前のすべてのパッケージ変数を指すポインタを格納している

    型グロブを別の型グロブに代入することで、同じデータの別名を作ることができる

    名前をファイルハンドルとして扱うとき、Perlは型グロブのファイルハンドル部を探す

    型グロブはシンボルテーブルを操作する
        つまり、パッケージ変数を処理する
open LOG_FH, ">> log.txt" or die $!;
log_message( *LOG_FH, 'log message' );
close LOG_FH;

sub log_message {
    local *FH = shift;

    print FH @_, "\n";
}
上記のコードで、localを使う理由
    パッケージ変数はレキシカル変数になれないので、FHをmyで宣言することはできない

    localを付けないFHに代入した場合
        スクリプト内のどこかで、FHという名前が付けられているものを壊してしまう

    localを付けるたFHに代入した場合
        log_messageサブルーチンの実行中、FHという名前には一時的な値が格納される
            サブルーチンから制御が戻るときに、PerlはFHを元の値に戻す

これらのやり方は「古い方法」

8.2 改良された方法

openで、ファイルハンドルリファレンスを作れるように
    Perl5.6以降から

    ファイルハンドルに指定できるのは、値がundefのスカラー変数
        スカラー変数がundefでなかった場合
            strictプラグマを使用しているとエラーとなり動作しない

    ファイルハンドルに指定する変数は、openの中で宣言可能

    飾りなしのファイルハンドルを使っていた箇所で、
    スカラー変数のファイルハンドルリファレンスを使える

    スカラー変数のファイルハンドルリファレンスは、
    スコープから外れる、または、他の値が代入されるとファイルをクローズする
        明示的にクローズする必要はない
# openの中で変数の宣言
open my $log_fh, ">> log.txt" or die $!;

log_message( $log_fh, "log message");

sub log_message {
    my $fh = shift;

    # 出力
    print $fh @_, "\n";
}
8.3 さらによい方法
3引数形式のopenを使う
    2引数形式では、第2引数で指定した文字列をPerlが正しく解釈しない可能性がある
# 3引数形式のopen
open my $log_fh, '>>', 'log.txt' or die $!;
8.4 IO::Handle
IO::Handle
    入出力全般の基底クラス
        ファイルだけではなく、多くのものを処理する

    Perlは、裏でIO::Handleモジュールを使用している
        ファイルハンドルスカラーは、実際はオブジェクト

    新しいIOモジュールを作ること以外で、IO::Handleは直接使わないようにするべき
        IO::Handleの派生モジュールを使用するべき

    Perl組み込みのopenですでにできることも含まれている
        できることは、Perlのバージョンで異なる

        組み込み関数でできることを、モジュールインターフェイスで利用する理由
            どのモジュールを使い入出力を行うのか先延ばしできる
            モジュール名を変更するだけで、動作の切り替えができる
8.4.1 IO::File
IO::File
    IO::Handleの派生クラス
        ファイルを操作する

    Perlのコアモジュール
use IO::File;

# 1引数形式のコンストラクタ
my $fh = IO::File->new( '> log.txt' ) || die $!;

# 2引数形式のコンストラクタ
#   第2引数はファイルハンドルのアクセスモード
#       ANSI Cのfopenのモード文字列
#           モード文字列は組み込みのopenでも使える
my $read_fh = IO::File->new( 'log.txt', 'r' );
my $write_fh= IO::File->new( 'log.txt', 'w' );

# アクセスモード引数にビットマスクを使うことで、より細かく動作をコントロール可能
#   定数は、IO::Fileモジュールが提供
my $append_fh = IO::File->new( 'log.txt', O_WRONLY|O_APPEND );

# 一時ファイルの作成
#   読み書き用のファイルハンドルを取得できる
my $temp_fh = IO::File->new_tmpfile;

# 明示的にクローズ
$temp_fh->close;
undef $append_fh;

# 明示的にクローズしない場合、
# ファイルハンドルは、スコープから外れるとクローズされる
8.4.2 無名IO::Fileオブジェクト
IO::Fileオブジェクトは、スカラー変数以外にも代入可能
    配列の要素
    ハッシュの値
8.4.3 IO::Scalar
IO::Scalar
    スカラーに情報を追加できるファイルハンドルリファレンスを作成する

    Perlのコアモジュールではない
use IO::Scalar;

my $string_log  = '';
my $scalar_fh   = IO::Scalar->new( \$string_log );

# メッセージは、$string_logに格納される
print $scalar_fh "Hello, IO::Scalar!\n";
print $scalar_fh "Hello, IO::Scalar!!\n";
print $scalar_fh "Hello, IO::Scalar!!!\n";

$scalar_fh = IO::Scalar->new( \$string_log );

while ( <$scalar_fh> ) {
    print;
}
# Perl5.8以降では、IO::Scalarを使わずに同じことが可能
my $string_log = ();
open( my $fh, '>>', \$string_log ) || die $!;

print $fh "not use IO::Scalar!\n";
print $fh "not use IO::Scalar!!\n";
print $fh "not use IO::Scalar!!!\n";
print $fh "not use IO::Scalar!!!!\n";

open( $fh, '<', \$string_log ) || die $!;
while ( <$fh> ) {
    print;
}
8.4.4 IO::Tee
IO::Tee
    出力を多重化できる

    第1引数が入力ファイルハンドルの場合
        Teeファイルハンドルを使い、入力ファイルからの読み出しと出力ファイルへの書き込みの両方が可能
            第1引数以外は、出力ファイルハンドルである必要がある
use IO::File;
use IO::Scalar;
use IO::Tee;

# 出力の多重化
{
    my $log_fh1 = IO::File->new( 'log1.txt', 'w' );
    my $log_fh2 = IO::File->new( 'log2.txt', 'w' );
    my $tee_fh = IO::Tee->new( $log_fh1, $log_fh2 );
    print $tee_fh "output tee_fh!\n";
}


# 第一引数が入力ファイルハンドル
{
    my $read_fh = IO::File->new( 'log.txt', 'r' );
    my $log_fh1 = IO::File->new( 'log1.txt', 'w' );
    my $log_fh2 = IO::File->new( 'log2.txt', 'w' );

    my $tee_fh = IO::Tee->new( $read_fh, $log_fh1, $log_fh2 );

    while ( <$tee_fh> ) {};

    print $tee_fh "end\n";
}
8.5 ディレクトリハンドルリファレンス
# ファイルハンドルリファレンスと同様に、
# ディレクトリハンドルリファレンスの作成も可能
opendir my $dh, '.' or die $!;
for my $file ( readdir( $dh ) ){
    print $file, "\n";
}
8.5.1 IO::Dir
IO::Dir
    Perlコアモジュール
        Perl5.6以降から

    Perlの組み込み機能をオブジェクト指向インターフェイスで提供
use IO::Dir;

my $dir_fh = IO::Dir->new( '.' ) || die $!;

while( defined( my $file = $dir_fh->read ) ) {
    print "$file\n";
}

感想

IO::Teeの第一引数が入力ファイルハンドルのものが、動作が想像してたものと違った。
本のニュアンスでは、明示的に書き込まないと書き込まれないように受け取れたが、
実際サンプルコードを動かすと、読み込むと同時に書き込みが行われているようだ。
あとでちゃんと調べよう。

続・初めてのPerl 改訂版

続・初めてのPerl 改訂版