[ ↑ INDEX ] [ ← PREV ] [ NEXT → ]

Lesson 4 ファイルへの入出力


■0. 準備

script/kwic2.plvoa/* を対象として実行する。(後ろに "| more" や "| less" は付けないこと。)

   % perl script/kwic2.pl voa/*         

"SEARCH STRING: " と表示されるので,"\bjust\b" とタイプし [ENTER] を入力して,結果がどうなるか確認する。(引用符をタイプしないこと。)

kwic2.pl では,前回使用した kwic1.pl とは違い,スクリプトの実行中に,検索文字列をキーボードで入力して指定する。スクリプトの内容を表示し,kwic1.pl とどこが違うか確認する。


■1. 標準入力・標準出力・標準エラー出力

標準入力・標準出力・標準エラー出力は,それぞれハンドル名 STDINSTDOUTSTDERR で指定し利用できる。スクリプトが終了すると自動的に閉じられる。

STDIN 標準入力:キーボードからの入力。
STDOUT 標準出力:ディスプレー (コンソール)。
STDERR 標準エラー出力:ディスプレー (コンソール)。

デフォルトの入力先・出力先

<> 指定がないとコマンドラインで指定された入力ファイルが入力元となる。入力ファイルの指定がない場合には,標準入力 (STDIN) が対象となる。
print 指定がないと STDOUT が出力先となる。

print; 」=「 print $_; 」=「 print STDOUT $_;

キーボードからデータを読み込む

スクリプトの実行中にキーボードからデータを読み込むには,以下のようにすればよい。代入演算子 = の左辺に指定された変数に値が代入される。

   $var = <STDIN>;

読み込んだデータには [改行] ("\n") が含まれているので,まず最初に chomp で [改行] を削除することが多い。

ディスプレーに表示する

STDOUT に出力すればディスプレーに表示される。しかし,リダイレクトすると STDOUT は出力先として指定されたファイルに結びつけられてしまうので,STDOUT に出力してもディスプレーには表示されなくなる。データの処理の出力先とは別に,ディスプレー自体に表示したいのであれば,STDERR を指定する。

   出力 ──→ STDERR ──→ ディスプレー
   出力 ──→ STDOUT ──→ ディスプレー

 リダイレクトすると...

   出力 ──→ STDERR ──→ ディスプレー
   出力 ──→ STDOUT ┬×→ ディスプレー
              └─→ ファイル

◇ スクリプトの実行

次のスクリプトを実行し,結果を確認しなさい。なぜそのような結果になるのか,スクリプトの各行の意味を確認しなさい。

   script/hi.pl
   --------------------------------------------------
   print STDOUT "Type your name: ";
   $name = <STDIN>;
   chomp $name;
   print STDOUT "Hi, $name. How are you?\n";
   exit;
   --------------------------------------------------
                                               [解説]
   script/echo.pl
   --------------------------------------------------
   print STDOUT "Type any word you like ([ENTER] to exit)\n\n";
   
   while (<STDIN>) {
      chomp;
      if (/^\s*$/) { print "Bye!\n\n"; exit; }
      print "$_ .. $_ ...";
      while (s/.//) {
         print " $_ ....";
      }
      print "..\n\n";
   }
   
   exit;
   --------------------------------------------------
                                               [解説]

■2. 入出力用にファイルを開く

ファイルの open,close とハンドル

コマンドラインで指定した入力・出力以外のファイルを扱う場合には,ハンドル名を付けて開く。ハンドル名は,<> でレコードを読み込むファイルや, print で出力するファイルを指定したりするのに使われる。

ファイルを開く

ファイルを次のように指定する。「ハンドル, レイヤー,ファイル」部分を ( ) で括ってもよい。

   open ハンドル, レイヤー, ファイル
ハンドル  以後のスクリプトでファイルを指定するのに使うもの。INOUT のように,全て大文字で書くようにすると,関数や変数などとぶつかることはないので安心である。

ファイル  用途に応じてファイル名の最初に以下の記号を付ける。何も付けないときは,読み出し用として開かれる。

レイヤー 読み出し用,書き出し用などの指定を行う。 (文字コード,改行コードなどの指定もできるが,ここでは触れない。)
< 読み出し用に開く。
> 書き込み用に開く。ファイルが存在しない場合には,新規にファイルを作成する。既に同じ名前のファイルが存在する場合には上書きする。
>> 書き込み用に開く。ファイルが存在しない場合には,新規にファイルを作成する。既に同じ名前のファイルが存在する場合には,ファイル末に書き加える。

入力用,出力用にファイルを開く場合には,次のように指定することになる。

   open ハンドル, "<", 入力ファイル;
   open ハンドル, ">", 出力ファイル;    #上書き
   open ハンドル, ">>", 出力ファイル;   #追加

open に続き,or die または || die と書けば,指定したファイルが存在しないなどの理由でファイルを開くことができなかった場合に,その後に処理を行わずにスクリプトを終了させることができる。

   open (IN, "<", "file.txt") or die;

[open 関数: 補足]

ファイルを閉じる
   close ハンドル名

ファイルを閉じる場合には,ハンドル名を指定すればよい。1つのハンドル名には1つのファイルが結びつけられるので,ハンドル名のみでファイルが特定できる。既に使われているハンドル名で別のファイルを開いた場合には,先に開かれていたファイルは自動的に閉じられる。また,close で明示的に閉じなくとも,スクリプトが終了すると開かれているファイルは自動的に閉じられる。


■3. ファイルからの読み込み・ファイルへの書き込み

読み込み

<IN> のように,ハンドル名をダイヤモンド演算子 <> で囲むと,ファイルから1レコード分データを読み出す。次のようすれば,レコードの内容は変数に代入される。

   $var = <IN>;

ダイヤモンド演算子 <> は1レコードしか読み込まないので,すべてのレコードを順番に読み込むには while と組み合わせて使う。

   while ( $var = <IN> ) { 処理 }

while の条件部分で代入先を指定しないでレコードを読み込むと,$_ に代入される。これまで,下の左のように書いてきたものは,明示的に示すと右のようになる。

   while ( <> ) { 処理 }  =  while ( $_ = <> ) { 処理 }

$/ の値を変更することで,様々な単位でデータを読み込むことができる。「undef $/;」あるいは「$/ = undef;」とすると $/ の値が未定義値となり,ファイル中のデータを全て読み込む。

書き込み

print とデータ(文字列,数値,変数など)の間にハンドル名を書くと,そのファイルにデータを書き込むことができる。複数のデータを書き込むときは,コンマで区切って並べる。ハンドル名の後にコンマを入れないこと。

   print ハンドル名 データ [, データ2, データ3, ...]

◇ スクリプトの実行

1. 次のスクリプトを実行し,結果を確認しなさい。なぜそのような結果になるのか,スクリプトの各行の意味を確認しなさい。

   script/hare-tort.pl
   --------------------------------------------------
   open (IN,  "<", "data/aesop.txt") or die;
   open (OUT, ">", "hare_and_tortoise.txt") or die;

   $/ = "\n\n\n";

   while(<IN>){
      if (/\bhares?\b/i and /\btortoises?\b/i) {
         print OUT;
      }
   }

   exit;
   --------------------------------------------------
                                               [解説]

2. ディレクトリ data にある nihongoA.cha, nihongoB.cha の内容を確認しなさい。次に merge.pl を実行し,結果を確認しなさい。なぜそのような結果になるのか,スクリプトの各行の意味を確認しなさい。

   % perl script/merge.pl

   script/merge.pl
   --------------------------------------------------
   $fileA = "data/nihongoA.cha";
   $fileB = "data/nihongoB.cha";

   open (INA, "<", $fileA) or die;
   open (INB, "<", $fileB) or die;

   while ($textA = <INA> and $textB = <INB>) {
      if ($textA eq $textB) {
         $textA =~ s/^\*/\n\*/;
         print $textA;
      } else {
         chomp $textA; print "$textA ($fileA)\n";
         chomp $textB; print "$textB ($fileB)\n";
      }
   }
   
   exit;
   --------------------------------------------------
                                               [解説]

■4. 特殊ファイルハンドル DATA

ファイルハンドル DATA を用いると,スクリプトファイル中にデータを書き込み,<DATA> でデータを読み込むことが出来る。データは __END__ または __DATA__ 以降に書く。

   --------------------------------------------------
   while (<DATA>) {
       tr/A-Z/a-z/;
       print;
   }

   exit;

   __END__
   Peter Piper picked a peck of pickled pepper.
   A peck of pickled pepper Peter Piper picked.

   --------------------------------------------------


   出力結果
   --------------------------------------------------
   peter piper picked a peck of pickled pepper.
   a peck of pickled pepper peter piper picked.

   --------------------------------------------------

□ 練習問題

練習問題A:必ずやること。

  1. kwic2.pl の最初の3行の意味を確認しなさい。

  2. kwic2.pl を書き換えて,検索文字列の左右の文脈の長さもキーボードから入力して指定できるようにしなさい。

練習問題B:余裕があったらやってみよう。

  1. 練習Aで書き換えたスクリプトに,さらに次の2つの機能を付け加えなさい。

    1. 検索文字列として不適切なものが指定されたら,メッセージを出力して終了する。
    2. 右と左の文脈の長さの最小値・最大値を設定し,最小値より小さい値が指定されたときは最小値を,最大値より大きな値が指定された場合には最大値を値とするようにする。長さが指定されなかった場合にはデフォルトの値 (たとえば 25) を使う。
  2. merge.pl を実行した結果を見ると,最後の行の @End が前の行と空行で分けられていない。@End とその前のデータが空行で分離されるよう,スクリプトを書き換えなさい。(前回までに学んだことを使って書き換えることができる。)


[ ↑ INDEX ] [ ← PREV ] [ NEXT → ]