=encoding utf8 =head1 NAME =begin original perlfaq5 - Files and Formats =end original perlfaq5 - ファイルとフォーマット =head1 DESCRIPTION =begin original This section deals with I/O and the "f" issues: filehandles, flushing, formats, and footers. =end original このセクションでは、入出力と“f”に関する事柄: ファイルハンドル (filehandle)、フラッシング(flushing)、フォーマット(format)、 フッター(footer)を扱います。 =head2 How do I flush/unbuffer an output filehandle? Why must I do this? X X X X (出力ファイルハンドルを flush/unbufferするには? なぜ私はこれをやらなければならないの?) =begin original (contributed by brian d foy) =end original (brian d foy によって寄贈されました) =begin original You might like to read Mark Jason Dominus's "Suffering From Buffering" at L . =end original L にある Mark Jason Dominus の "Suffering From Buffering" を読みたいかもしれません。 =begin original Perl normally buffers output so it doesn't make a system call for every bit of output. By saving up output, it makes fewer expensive system calls. For instance, in this little bit of code, you want to print a dot to the screen for every line you process to watch the progress of your program. Instead of seeing a dot for every line, Perl buffers the output and you have a long wait before you see a row of 50 dots all at once: =end original 通常 Perl は出力をバッファリングするので、ちょっと出力する毎に システムコールを呼び出したりはしません。 出力回数を節約することで、負荷の高いシステムコールの回数を減らします。 例えば、このコード片では、プログラムが1行の処理する毎に、プログラムの 実行状況を示すためにドットを表示します。 Perl は 行毎にドットを表示せず、出力をバッファリングするので、 長い間待った後に 50 文字のドットが一気に表示されます: # long wait, then row of dots all at once while( <> ) { print "."; print "\n" unless ++$count % 50; #... expensive line processing operations } =begin original To get around this, you have to unbuffer the output filehandle, in this case, C. You can set the special variable C<$|> to a true value (mnemonic: making your filehandles "piping hot"): =end original これを回避するには、出力ファイルハンドル (この場合は C) の バッファリングを留める必要があります。 特殊変数 C<$|> を真の値にセットします (記憶法: パイプをホットな状態にしておくために使う): $|++; # dot shown immediately while( <> ) { print "."; print "\n" unless ++$count % 50; #... expensive line processing operations } =begin original The C<$|> is one of the per-filehandle special variables, so each filehandle has its own copy of its value. If you want to merge standard output and standard error for instance, you have to unbuffer each (although STDERR might be unbuffered by default): =end original C<$|> はファイルハンドル単位の特殊変数の一つで、ファイルハンドル毎に この値のコピーを保持しています。 例えば、標準出力と標準エラーをまとめたいなら、それぞれのバッファリングを 解除する必要があります(但し、STDERR はデフォルトでは バッファリングしていないはずです): { my $previous_default = select(STDOUT); # save previous default $|++; # autoflush STDOUT select(STDERR); $|++; # autoflush STDERR, to be sure select($previous_default); # restore previous default } # now should alternate . and + while( 1 ) { sleep 1; print STDOUT "."; print STDERR "+"; print STDOUT "\n" unless ++$count % 25; } =begin original Besides the C<$|> special variable, you can use C to give your filehandle a C<:unix> layer, which is unbuffered: =end original C<$|> 特殊変数に加えて、C を使ってファイルハンドルに C<:unix> 層をつけることでバッファリングしないようにできます: binmode( STDOUT, ":unix" ); while( 1 ) { sleep 1; print "."; print "\n" unless ++$count % 50; } =begin original For more information on output layers, see the entries for C and L in L, and the L module documentation. =end original 出力層に関するさらなる情報については、L の C および L のエントリと、L モジュールの文書を参照してください。 =begin original If you are using L or one of its subclasses, you can call the C method to change the settings of the filehandle: =end original L かその派生クラスを使っているなら、ファイルハンドルの設定を 変更するために C メソッドを呼び出せます: use IO::Handle; open my( $io_fh ), ">", "output.txt"; $io_fh->autoflush(1); =begin original The L objects also have a C method. You can flush the buffer any time you want without auto-buffering =end original L オブジェクトには C メソッドもあります。 いつでも自動バッファリングなしにバッファをフラッシュできます $io_fh->flush; =head2 How do I change, delete, or insert a line in a file, or append to the beginning of a file? X (ファイルの一行を変更する/ファイルのある行を削除する/ファイルの中程で一行挿入する/ファイルの先頭に追加するには?) =begin original (contributed by brian d foy) =end original (brian d foy によって寄贈されました) =begin original The basic idea of inserting, changing, or deleting a line from a text file involves reading and printing the file to the point you want to make the change, making the change, then reading and printing the rest of the file. Perl doesn't provide random access to lines (especially since the record input separator, C<$/>, is mutable), although modules such as L can fake it. =end original テキストファイルの行を挿入、変更、削除するための基本的な考え方は、 ファイルを変更したい地点まで読み込んで表示し、変更を行い、ファイルの 残りを読み込んで表示するというものです。 (特にレコード入力セパレータ C<$/> は変更可能なので) Perl は行に対する ランダムアクセスは提供していませんが、L はそれを見せかけます。 =begin original A Perl program to do these tasks takes the basic form of opening a file, printing its lines, then closing the file: =end original この作業をする Perl プログラムは、ファイルを開いて、一行毎に表示し、 ファイルを閉じるという基本的な形をとります: open my $in, '<', $file or die "Can't read old file: $!"; open my $out, '>', "$file.new" or die "Can't write new file: $!"; while( <$in> ) { print $out $_; } close $out; =begin original Within that basic form, add the parts that you need to insert, change, or delete lines. =end original 基本形の中に、行の追加、修正、削除という必要な作業を追加します。 =begin original To prepend lines to the beginning, print those lines before you enter the loop that prints the existing lines. =end original 先頭に追加するには、既存の行を表示するループに入る前に追加行を表示します。 open my $in, '<', $file or die "Can't read old file: $!"; open my $out, '>', "$file.new" or die "Can't write new file: $!"; print $out "# Add this line to the top\n"; # <--- HERE'S THE MAGIC while( <$in> ) { print $out $_; } close $out; =begin original To change existing lines, insert the code to modify the lines inside the C loop. In this case, the code finds all lowercased versions of "perl" and uppercases them. The happens for every line, so be sure that you're supposed to do that on every line! =end original 既にある行を変更するには、C の中に行を変更するコードを追加します。 今回の場合、コードは小文字の "perl" を探してそれを大文字にします。 これは全ての行に対して起こるので、全ての行に対してこれを行いたいということを 確認してください! open my $in, '<', $file or die "Can't read old file: $!"; open my $out, '>', "$file.new" or die "Can't write new file: $!"; print $out "# Add this line to the top\n"; while( <$in> ) { s/\b(perl)\b/Perl/g; print $out $_; } close $out; =begin original To change only a particular line, the input line number, C<$.>, is useful. First read and print the lines up to the one you want to change. Next, read the single line you want to change, change it, and print it. After that, read the rest of the lines and print those: =end original 特定の行だけ変更するには、入力行番号 C<$.> が便利です。 まず、変更したい行まで読み込んで表示します。 次に、変更したい行を 1 行読んで、変更し、表示します。 その後、残りの行を読み込んで表示します: while( <$in> ) { # print the lines before the change print $out $_; last if $. == 4; # line number before change } my $line = <$in>; $line =~ s/\b(perl)\b/Perl/g; print $out $line; while( <$in> ) { # print the rest of the lines print $out $_; } =begin original To skip lines, use the looping controls. The C in this example skips comment lines, and the C stops all processing once it encounters either C<__END__> or C<__DATA__>. =end original 行を読み飛ばすには、ループ制御を使います。 この例の C はコメント行を読み飛ばし、C は、C<__END__> か C<__DATA__> のどちらかに出会った後は全ての処理を停止します。 while( <$in> ) { next if /^\s+#/; # skip comment lines last if /^__(END|DATA)__$/; # stop at end of code marker print $out $_; } =begin original Do the same sort of thing to delete a particular line by using C to skip the lines you don't want to show up in the output. This example skips every fifth line: =end original 同じようなことは、C を使って表示したくない行を飛ばすことで行えます。 この例は 5 行毎に飛ばします: while( <$in> ) { next unless $. % 5; print $out $_; } =begin original If, for some odd reason, you really want to see the whole file at once rather than processing line-by-line, you can slurp it in (as long as you can fit the whole thing in memory!): =end original もし、なんらかの変わった理由により、一行ずつ処理するのではなく、本当に ファイル全体を一度に読み込みたい場合、(メモリがあれば!)以下のようにして できます: open my $in, '<', $file or die "Can't read old file: $!" open my $out, '>', "$file.new" or die "Can't write new file: $!"; my $content = do { local $/; <$in> }; # slurp! # do your magic here print $out $content; =begin original Modules such as L and L can help with that too. If you can, however, avoid reading the entire file at once. Perl won't give that memory back to the operating system until the process finishes. =end original L や L のようなモジュールもこの助けになります。 しかし、もし可能なら、ファイル全体を一度に読み込むのは避けてください。 Perl は OS から確保したメモリを、プロセスが終わるまで返しません。 =begin original You can also use Perl one-liners to modify a file in-place. The following changes all 'Fred' to 'Barney' in F, overwriting the file with the new contents. With the C<-p> switch, Perl wraps a C loop around the code you specify with C<-e>, and C<-i> turns on in-place editing. The current line is in C<$_>. With C<-p>, Perl automatically prints the value of C<$_> at the end of the loop. See L for more details. =end original ファイルをその場で変更するために、Perl の一行野郎も使えます。 以下の例では、F にある全ての 'Fred' を 'Barney' に変更し、 ファイルを新しい内容で上書きします。 C<-p> スイッチ付きの場合、C<-e> で指定したコードを C ループで包み、 C<-i> はその場編集を有効にします。 現在行は C<$_> に入っています。 C<-p> があると、Perl はループの最後に C<$_> の値を自動的に表示します。 さらなる詳細については L を参照してください。 perl -pi -e 's/Fred/Barney/' inFile.txt =begin original To make a backup of C, give C<-i> a file extension to add: =end original C をバックアップするには、C<-i> で追加する拡張子を指定します: perl -pi.bak -e 's/Fred/Barney/' inFile.txt =begin original To change only the fifth line, you can add a test checking C<$.>, the input line number, then only perform the operation when the test passes: =end original 5 行目だけを変更するには、入力行番号 C<$.> をチェックするテストを追加し、 テストに通過した場合にのみ操作を行います: perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txt =begin original To add lines before a certain line, you can add a line (or lines!) before Perl prints C<$_>: =end original 特定の行の前に追加するには、Perl が C<$_> を表示する前に行を追加します: perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txt =begin original You can even add a line to the beginning of a file, since the current line prints at the end of the loop: =end original 現在行はループの終わりに表示されるので、ファイルの先頭に行を 追加することもできます: perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txt =begin original To insert a line after one already in the file, use the C<-n> switch. It's just like C<-p> except that it doesn't print C<$_> at the end of the loop, so you have to do that yourself. In this case, print C<$_> first, then print the line that you want to add. =end original 既にファイルにある行の後に行を追加するには、C<-n> スイッチを使います。 これは C<-p> と同様ですが、ループの終わりに C<$_> を表示しないので、 自分自身で表示する必要があります。 この場合、まず C<$_> を表示し、それから追加したい行を表示します。 perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txt =begin original To delete lines, only print the ones that you want. =end original 行を削除するには、必要なものだけを表示します: perl -ni -e 'print if /d/' inFile.txt =head2 How do I count the number of lines in a file? X X X (あるファイルの行数を数えるには?) =begin original (contributed by brian d foy) =end original (brian d foy によって寄贈されました) =begin original Conceptually, the easiest way to count the lines in a file is to simply read them and count them: =end original 概念的には、ファイルの行数を数える一番易しい方法は単にそれを読み込んで それを数えます: my $count = 0; while( <$fh> ) { $count++; } =begin original You don't really have to count them yourself, though, since Perl already does that with the C<$.> variable, which is the current line number from the last filehandle read: =end original しかし、Perl には最後に読み込んだファイルハンドルの現在の行番号である C<$.> 変数が既にあるので、自分自身で数える必要はありません: 1 while( <$fh> ); my $count = $.; =begin original If you want to use C<$.>, you can reduce it to a simple one-liner, like one of these: =end original C<$.> を使いたいなら、以下のように単純な一行野郎にまで短くできます: % perl -lne '} print $.; {' file % perl -lne 'END { print $. }' file =begin original Those can be rather inefficient though. If they aren't fast enough for you, you might just read chunks of data and count the number of newlines: =end original しかしこれらはやや非効率です。 これがあなたにとって十分に速くないなら、単にデータの塊を読み込んで改行の 数を数えるかもしれません: my $lines = 0; open my($fh), '<:raw', $filename or die "Can't open $filename: $!"; while( sysread $fh, $buffer, 4096 ) { $lines += ( $buffer =~ tr/\n// ); } close FILE; =begin original However, that doesn't work if the line ending isn't a newline. You might change that C to a C so you can count the number of times the input record separator, C<$/>, shows up: =end original しかし、これは行の末尾が改行でない場合は動作しません。 C を C に変えて、入力レコードセパレータ C<$/> が 現れた数を数えるかもしれません: my $lines = 0; open my($fh), '<:raw', $filename or die "Can't open $filename: $!"; while( sysread $fh, $buffer, 4096 ) { $lines += ( $buffer =~ s|$/||g; ); } close FILE; =begin original If you don't mind shelling out, the C command is usually the fastest, even with the extra interprocess overhead. Ensure that you have an untainted filename though: =end original シェルに出るのを気にしないなら、C コマンドが、プロセス間の オーバーヘッドがあっても普通は最速です。 しかし、汚染されていないファイル名を使うように気をつけてください: #!perl -T $ENV{PATH} = undef; my $lines; if( $filename =~ /^([0-9a-z_.]+)\z/ ) { $lines = `/usr/bin/wc -l $1` chomp $lines; } =head2 How do I delete the last N lines from a file? X X (ファイルの最後の N 行を削除するには?) =begin original (contributed by brian d foy) =end original (brian d foy によって寄贈されました) =begin original The easiest conceptual solution is to count the lines in the file then start at the beginning and print the number of lines (minus the last N) to a new file. =end original 概念的に最も簡単な解決法は、ファイルの行数を数えて、最初から行数分 (マイナス最後の N) だけ新しいファイルに出力します。 =begin original Most often, the real question is how you can delete the last N lines without making more than one pass over the file, or how to do it without a lot of copying. The easy concept is the hard reality when you might have millions of lines in your file. =end original ほとんどの場合、実際の質問は、ファイルに対して 1 パスで最後の N 行を 削除する方法、または多くのコピーなしに行う方法です。 簡単な概念は、ファイルに何百万行もある場合は現実性がありません。 =begin original One trick is to use L, which starts at the end of the file. That module provides an object that wraps the real filehandle to make it easy for you to move around the file. Once you get to the spot you need, you can get the actual filehandle and work with it as normal. In this case, you get the file position at the end of the last line you want to keep and truncate the file to that point: =end original 一つの裏技は L を使って、ファイルの最後から 始めることです。 このモジュールは、ファイルを動き回りやすくするために実際の ファイルハンドルをラップしたオブジェクトを提供します。 必要な場所が分かれば、実際のファイルハンドルを取得して、いつも通りに 処理します。 この場合、保持したい最後の行の最後のファイル位置を取得して、 その位置でファイルを切り詰めます: use File::ReadBackwards; my $filename = 'test.txt'; my $Lines_to_truncate = 2; my $bw = File::ReadBackwards->new( $filename ) or die "Could not read backwards in [$filename]: $!"; my $lines_from_end = 0; until( $bw->eof or $lines_from_end == $Lines_to_truncate ) { print "Got: ", $bw->readline; $lines_from_end++; } truncate( $filename, $bw->tell ); =begin original The L module also has the advantage of setting the input record separator to a regular expression. =end original L モジュールは、入力レコードセパレータを 正規表現で設定できるという利点もあります。 =begin original You can also use the L module which lets you access the lines through a tied array. You can use normal array operations to modify your file, including setting the last index and using C. =end original 行に対して tie された配列を使ってアクセスできる L モジュールも 使えます。 最後の添え字を設定して C を使うなど、、ファイルを修正するために 通常の配列操作が使えます。 =head2 How can I use Perl's C<-i> option from within a program? X<-i> X (プログラム内から Perl の C<-i> オプションを使うには?) =begin original C<-i> sets the value of Perl's C<$^I> variable, which in turn affects the behavior of C<< <> >>; see L for more details. By modifying the appropriate variables directly, you can get the same behavior within a larger program. For example: =end original C<-i> は Perl の C<$^I> 変数の値をセットし、これにより C<< <> >> の 振る舞いに影響を与えます; 更なる詳細については L を 参照してください。 適切な変数を直接修正することによって、より大きなプログラムの中で同じ効果が 得られます。 例えば: # ... { local($^I, @ARGV) = ('.orig', glob("*.c")); while (<>) { if ($. == 1) { print "This line should appear at the top of each file\n"; } s/\b(p)earl\b/${1}erl/i; # Correct typos, preserving case print; close ARGV if eof; # Reset $. } } # $^I and @ARGV return to their old values here =begin original This block modifies all the C<.c> files in the current directory, leaving a backup of the original data from each file in a new C<.c.orig> file. =end original このブロックはカレントディレクトリの全ての the C<.c> ファイルを修正し、 各ファイルの元データのバックアップを新しい C<.c.orig> ファイルに残します。 =head2 How can I copy a file? X X X (ファイルをコピーするには?) =begin original (contributed by brian d foy) =end original (brian d foy によって寄贈されました) =begin original Use the L module. It comes with Perl and can do a true copy across file systems, and it does its magic in a portable fashion. =end original L モジュールをつかいましょう。 これは Perl に同梱されていて、ファイルシステム間で真のコピーが行われ、 移植性の面での細工がされています。 use File::Copy; copy( $original, $new_copy ) or die "Copy failed: $!"; =begin original If you can't use L, you'll have to do the work yourself: open the original file, open the destination file, then print to the destination file as you read the original. You also have to remember to copy the permissions, owner, and group to the new file. =end original L が使えない場合、作業を自分自身でする必要があります: 元ファイルを開き、宛て先ファイルを開き、元ファイルから読んだものを宛て先 ファイルに書き込みます。 権限、所有者、グループも新しいファイルにコピーすることを忘れないように しなければなりません。 =head2 How do I make a temporary file name? X (一時ファイルの名前を作り出すには?) =begin original If you don't need to know the name of the file, you can use C with C in place of the file name. In Perl 5.8 or later, the C function creates an anonymous temporary file: =end original もしファイル名を知る必要がないなら、C でファイル名の部分に C を指定します。 Perl 5.8 以降では、C 関数は無名一時ファイルを作成します。 open my $tmp, '+>', undef or die $!; =begin original Otherwise, you can use the File::Temp module. =end original さもなければ、File::Temp モジュールが使えます。 use File::Temp qw/ tempfile tempdir /; my $dir = tempdir( CLEANUP => 1 ); ($fh, $filename) = tempfile( DIR => $dir ); # or if you don't need to know the filename my $fh = tempfile( DIR => $dir ); =begin original The File::Temp has been a standard module since Perl 5.6.1. If you don't have a modern enough Perl installed, use the C class method from the IO::File module to get a filehandle opened for reading and writing. Use it if you don't need to know the file's name: =end original File::Temp は Perl 5.6.1 以降標準モジュールになっています。 十分にモダンな Perl がインストールされていないなら、 IO::File モジュールにあるクラスメソッド C を使って 読み書きのためにオープンされたファイルハンドルを取得します。 ファイルがどんな名前なのかを知る必要がない場合はこれを使ってください。 use IO::File; my $fh = IO::File->new_tmpfile() or die "Unable to make new temporary file: $!"; =begin original If you're committed to creating a temporary file by hand, use the process ID and/or the current time-value. If you need to have many temporary files in one process, use a counter: =end original 一時ファイルの作成を手作業で行いたいのなら、 プロセスIDやその時点での時刻(あるいはこれら両方)を使用してください。 一つのプロセスで複数の一時ファイルを使用するのであれば、 カウンターを使用しましょう: BEGIN { use Fcntl; my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMPDIR} || $ENV{TEMP}; my $base_name = sprintf "%s/%d-%d-0000", $temp_dir, $$, time; sub temp_file { my $fh; my $count = 0; until( defined(fileno($fh)) || $count++ > 100 ) { $base_name =~ s/-(\d+)$/"-" . (1 + $1)/e; # O_EXCL is required for security reasons. sysopen $fh, $base_name, O_WRONLY|O_EXCL|O_CREAT; } if( defined fileno($fh) ) { return ($fh, $base_name); } else { return (); } } } =head2 How can I manipulate fixed-record-length files? X X (固定長レコードのファイルを操作するには?) =begin original The most efficient way is using L and L. This is faster than using L when taking many, many strings. It is slower for just a few. =end original 最も効率的なやり方はL と L を使うものです。 これは文字列が大量にあるときにはL を 使うよりも高速です。 速度低下もほとんどありません。 =begin original Here is a sample chunk of code to break up and put back together again some fixed-format input lines, in this case from the output of a normal, Berkeley-style ps: =end original 以下に挙げたのは幾つかの固定フォーマットをした入力行に対して 分解を行ったり書き戻しをするコード片の例で、出力は Berkeley形式のpsに準じています。 # sample input line: # 15158 p5 T 0:00 perl /home/tchrist/scripts/now-what my $PS_T = 'A6 A4 A7 A5 A*'; open my $ps, '-|', 'ps'; print scalar <$ps>; my @fields = qw( pid tt stat time command ); while (<$ps>) { my %process; @process{@fields} = unpack($PS_T, $_); for my $field ( @fields ) { print "$field: <$process{$field}>\n"; } print 'line=', pack($PS_T, @process{@fields} ), "\n"; } =begin original We've used a hash slice in order to easily handle the fields of each row. Storing the keys in an array makes it easy to operate on them as a group or loop over them with C. It also avoids polluting the program with global variables and using symbolic references. =end original それぞれの行のフィールドを簡単に扱うためにハッシュスライスを使いました。 キーを配列に保管するということで、これらをグループや C によるループで 簡単に扱えます。 これはまた、グローバル変数とシンボリックリファレンスの使用でプログラムが 汚染されることを防ぎます。 =head2 How can I make a filehandle local to a subroutine? How do I pass filehandles between subroutines? How do I make an array of filehandles? X X X (ファイルハンドルをサブルーチンに局所化するには? サブルーチンにファイルハンドルを渡すには? ファイルハンドルの配列を作るには?) =begin original As of perl5.6, open() autovivifies file and directory handles as references if you pass it an uninitialized scalar variable. You can then pass these references just like any other scalar, and use them in the place of named handles. =end original perl5.6 から、open() にファイルハンドルやディレクトリハンドルとして未定義の スカラ変数を渡すと、これをリファレンスとして自動有効化します。 その後これらのリファレンスはその他のスカラと同じように扱え、名前付き ハンドルを指定する場所で使えます。 open my $fh, $file_name; open local $fh, $file_name; print $fh "Hello World!\n"; process_file( $fh ); =begin original If you like, you can store these filehandles in an array or a hash. If you access them directly, they aren't simple scalars and you need to give C a little help by placing the filehandle reference in braces. Perl can only figure it out on its own when the filehandle reference is a simple scalar. =end original お好みなら、これらのファイルハンドルを配列やハッシュに保管することもできます。 これらを直接アクセスすると、これらは単なるスカラではないので、 ファイルハンドルを中かっこで囲むことで C に少し助けを与える必要が あります。 Perl はファイルハンドルリファレンスが単純なスカラの場合にのみファイル ハンドルリファレンスと認識します。 my @fhs = ( $fh1, $fh2, $fh3 ); for( $i = 0; $i <= $#fhs; $i++ ) { print {$fhs[$i]} "just another Perl answer, \n"; } =begin original Before perl5.6, you had to deal with various typeglob idioms which you may see in older code. =end original perl5.6 より前では、古いコードにあるような、様々な型グロブの慣用法を 扱わなければなりません。 open FILE, "> $filename"; process_typeglob( *FILE ); process_reference( \*FILE ); sub process_typeglob { local *FH = shift; print FH "Typeglob!" } sub process_reference { local $fh = shift; print $fh "Reference!" } =begin original If you want to create many anonymous handles, you should check out the Symbol or IO::Handle modules. =end original 大量の無名ハンドルを作りたいのであれば、 Symbol, FileHandle, IO::Handle といったモジュールを参照してください。 =head2 How can I use a filehandle indirectly? X (ファイルハンドルを間接的に扱うには?) =begin original An indirect filehandle is the use of something other than a symbol in a place that a filehandle is expected. Here are ways to get indirect filehandles: =end original 間接ファイルハンドルは、あるファイルハンドルを期待している場所に置かれた シンボル以外のなにかを使っています。 以下に間接ファイルハンドルの例を挙げます: $fh = SOME_FH; # bareword is strict-subs hostile $fh = "SOME_FH"; # strict-refs hostile; same package only $fh = *SOME_FH; # typeglob $fh = \*SOME_FH; # ref to typeglob (bless-able) $fh = *SOME_FH{IO}; # blessed IO::Handle from *SOME_FH typeglob =begin original Or, you can use the C method from one of the IO::* modules to create an anonymous filehandle and store that in a scalar variable. =end original あるいは、FileHandle モジュールや IO::* モジュールの C メソッドを使い、 それをスカラー変数に格納します。 use IO::Handle; # 5.004 or higher my $fh = IO::Handle->new(); =begin original Then use any of those as you would a normal filehandle. Anywhere that Perl is expecting a filehandle, an indirect filehandle may be used instead. An indirect filehandle is just a scalar variable that contains a filehandle. Functions like C, C, C, or the C<< >> diamond operator will accept either a named filehandle or a scalar variable containing one: =end original 上記のように作成して、後は普通のファイルハンドルと同じように使います。 Perlがファイルハンドルを期待しているところではどこでも 間接ファイルハンドルを使うことができるでしょう。 間接ファイルハンドルは、ファイルハンドルを保持しているスカラー変数に すぎません。C, C, Cのような関数や、 ダイヤモンド演算子 C<< >> はファイルハンドルもファイルハンドルを 保持しているスカラー変数の両方とも受け付けます。 ($ifh, $ofh, $efh) = (*STDIN, *STDOUT, *STDERR); print $ofh "Type it: "; my $got = <$ifh> print $efh "What was that: $got"; =begin original If you're passing a filehandle to a function, you can write the function in two ways: =end original ファイルハンドルを関数に渡した場合、渡される関数は二種類の書き方ができます: sub accept_fh { my $fh = shift; print $fh "Sending to indirect filehandle\n"; } =begin original Or it can localize a typeglob and use the filehandle directly: =end original 型グロブを局所化して、ファイルハンドルを直接使うことも可能です: sub accept_fh { local *FH = shift; print FH "Sending to localized filehandle\n"; } =begin original Both styles work with either objects or typeglobs of real filehandles. (They might also work with strings under some circumstances, but this is risky.) =end original 両方の形式とも、オブジェクトでも実際のファイルハンドルの型グロブでも 動作します。 (ある場合においては文字列でも可能ですが、これはちょっとリスクがあります。) accept_fh(*STDOUT); accept_fh($handle); =begin original In the examples above, we assigned the filehandle to a scalar variable before using it. That is because only simple scalar variables, not expressions or subscripts of hashes or arrays, can be used with built-ins like C, C, or the diamond operator. Using something other than a simple scalar variable as a filehandle is illegal and won't even compile: =end original 上の例では、ファイルハンドルを使う前にそれをスカラー変数に代入していました。 これは式でもなく、ハッシュや配列の添え字でもなく単純スカラ変数だけが CやC、ダイヤモンド演算子と一緒に使えるからです。 単純スカラ変数以外のものをファイルハンドルとして使うのは不正であり、 コンパイル時にエラーとなります: my @fd = (*STDIN, *STDOUT, *STDERR); print $fd[1] "Type it: "; # WRONG my $got = <$fd[0]> # WRONG print $fd[2] "What was that: $got"; # WRONG =begin original With C and C, you get around this by using a block and an expression where you would place the filehandle: =end original CやCの場合には、ブロックを使ってその中にファイルハンドルを 含む式を置くことによって対処することができます: print { $fd[1] } "funny stuff\n"; printf { $fd[1] } "Pity the poor %x.\n", 3_735_928_559; # Pity the poor deadbeef. =begin original That block is a proper block like any other, so you can put more complicated code there. This sends the message out to one of two places: =end original これらのブロックは妥当なものですから、より複雑なコードをその中に 入れこむことができます。 以下の例はメッセージを二ヶ所のどちらかひとつに送り出します: my $ok = -x "/bin/cat"; print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\n"; print { $fd[ 1+ ($ok || 0) ] } "cat stat $ok\n"; =begin original This approach of treating C and C like object methods calls doesn't work for the diamond operator. That's because it's a real operator, not just a function with a comma-less argument. Assuming you've been storing typeglobs in your structure as we did above, you can use the built-in function named C to read a record just as C<< <> >> does. Given the initialization shown above for @fd, this would work, but only because readline() requires a typeglob. It doesn't work with objects or strings, which might be a bug we haven't fixed yet. =end original このアプローチは C や C をオブジェクトメソッドの 呼び出しのように扱うものですが、これはダイヤモンド演算子には使えません。 なぜなら、ダイヤモンド演算子はカンマなしの引数を取る関数ではなくて本当の 演算子だからです。 さて、上記の例のように型グロブをあなたの作った構造に格納したとしましょう; C という名前の組み込み関数を使ってC<< <> >> が行うように レコードを読み込むことができます。 @fd を例にあったように初期化してやることでうまく動作します; しかし、これは readline() が型グロブを要求するからというだけです。 これはオブジェクトや文字列では動作しません; このことはバグとも言えるもので 現時点では修正されていません。 $got = readline($fd[0]); =begin original Let it be noted that the flakiness of indirect filehandles is not related to whether they're strings, typeglobs, objects, or anything else. It's the syntax of the fundamental operators. Playing the object game doesn't help you at all here. =end original 間接ファイルハンドルの妙な点はそれが文字列であるか、型グロブであるか、 オブジェクトであるか、はたまた別の何者であるかには関係しないということに 注意してください。 これは基本的な演算子の構文なのです。 オブジェクトをいじくりまわして遊ぶことはここでは何の助けにもなりません。 =head2 How can I set up a footer format to be used with write()? X