| Top Page | プログラミング | Perl 目次 | prev | next | 索引 |

11. sprintf の利用:ファイル名などの文字列を自動生成する

規則的なファイル名はプログラム中でつくろう

画面への文字や数値を画面に表示する関数 print と, 表示するときの書式をこまかく指定できる printf はすでに出てきました. 書式指定の仕方は printf のところで復習してください. もうひとつ,print 関数の一種に,sprintf というものがあります. これは,printf で画面に表示する内容そのものを文字列として返す関数です. と言っても分かりにくいですが,次のプログラムを実行してみれば分かるでしょう.

$x = 1.0 / 3.0;

print $x, "\n";              # 0.333333333333333 などと表示
printf ("%.2f\n", $x);       # 0.33 と表示(%.2f で小数点以下2ケタ表示)
$y = sprintf ("%.2f\n", $x); # $y の値は "0.33"
print $y, "\n";              # 0.33 と表示

sprintf は,プログラムのなかで何らかのルールにしたがって文字列を作りたい 場合に便利です.次の例では,ファイル名を作っています.

TH0001.txt から TH0100.txt まで100個のデータファイルを順次読んで, なんらかの処理をした結果をそれぞれ TH0001_A.txt から TH0100_A.txt に 記録したいとします. ひとつのファイルを処理するプログラム promram01.pl を書いて, コマンドラインで一回一回処理するファイルを指定して, 画面に表示されるものをリダイレクトするという手もあります. この場合,

perl program01.pl TH0001.txt > TH0001_a.txt
perl program01.pl TH0002.txt > TH0002_a.txt
....
perl program01.pl TH0100.txt > TH0100_a.txt
のように,延々と手でコマンドを打ち込むことになります. そんな単純作業はコンピュータにだってできる というわけで,コンピュータにやらせるようにプログラムを書いてみます.

$upto = 100;   #  何番のファイルまであるか.

for ($i = 1; $i <= $upto; ++$i) {  #  1 番から100 番まで.

    # %04d →,整数4ケタ,4ケタ未満の場合はあたまを 0 を埋める.
    $source_file = sprintf("TH%04d.txt", $i);   # 元データファイルの名前
    $target_file = sprintf("TH%04d_A.txt", $i); # 処理結果を記録するファイルの名前
    
    open (SOURCE, $source_file) or die "File $source_file not found.\n";
    open (TARGET, ">$target_file") or die "Failed to open $target_file for output\n";

    while ($line = <SOURCE>) {
        # この部分に,program01.pl でやっている処理を書く.
        #  出力は print $x, "\t" のように画面に表示するのではなく,
        #  print TARGET $x, "\t", .... のようにファイルに書き込む.
    }
    
    close SOURCE;
    close TARGET;
}

これで,同じようなコマンドライン入力を 100 回繰り返さなくてよくなりました. また,手入力につきもののキーの打ち間違いも避けられます. 楽をするための労を惜しんではいけません.


Perl のプログラムから他のプログラムを呼び出す

うえの例では,program01.pl という処理用プログラムそのものを 多数のファイルを一気に処理するように改造しました.

こんどは,program01.pl はもとのまま,ひとつのファイルを処理して 画面に表示するという設計にしておいて,このプログラムを何度も呼び出す ためのプログラムを別に作ることを考えてみます.そのためには system という 関数を利用すると便利です.

system は,引数として与えられた文字列を,そのまんま DOS 窓(あるいは 仮想端末など)のコマンドラインで入力したかのように実行するという関数です. たとえば,DOS窓で dir (UNIXなら ls )と入力するとカレントディレクトリ内のファイルの一覧が 表示されますが,Perl のプログラム中で

system ("dir");  

と書けば,やっぱりファイルの一覧が表示されます.

Perl のプログラム program01.pl を実行して TH0001.txt を処理させ, その結果を リダイレクトでTH0001_A.txt に書き込むには

perl program01.pl TH0001.txt > TH0001_a.txt

と書きました.ということは,別のプログラム test_system.pl で

system "perl program01.pl TH0001.txt > TH0001_a.txt";

と書けば,program01.pl に TH0001.txt を処理させて,その結果を TH0001_a.txt に記録することができます.それならば, program01.pl の中をいじって たくさんのファイルを自動処理させるようにするかわりに, program01.pl をなんども呼び出す別のプログラムを書くこともできそうです.

これがその例です.

$upto = 100;   #  何番のファイルまであるか.

for ($i = 1; $i <= $upto; ++$i) {  #  1 番から100 番まで.

    $source_file = sprintf("TH%04d.txt", $i);   # 元データファイルの名前
    $target_file = sprintf("TH%04d_A.txt", $i); # 処理結果を記録するファイルの名前

    #  system を使って,program01.pl を実行させる
    system "perl program01.pl $source_file > $target_file"; # 変数展開のワザ
}

system に渡す文字列のなかでは,変数展開を利用して, 繰り返しごとに違うファイル名を指定しています.

このような,単純な作業をするプログラムをいくつか書いて組み合わせて使う やりかただと,デバッグもやりやすいし,プログラムの使いまわしも やりやすくなります. たとえば,program01.pl が処理して作ったファイルをさらに処理するために プログラム program02.pl を書いたら,上のプログラムに2行ほど書き足すだけで もとファイルから二次処理までの一括処理ができます.

$upto = 100;   #  何番のファイルまであるか.

for ($i = 1; $i <= $upto; ++$i) {  #  1 番から100 番まで.

    $source_file = sprintf("TH%04d.txt", $i);   # 元データファイルの名前
    $target_file = sprintf("TH%04d_A.txt", $i); # 処理結果を記録するファイルの名前

    $target_file2 = sprintf("TH%04d_A.txt", $i); # 二次処理結果を記録するファイル
    system "perl program01.pl $source_file > $target_file";  # 一次処理
    system "perl program02.pl $target_file > $target_file2"; # 二次処理
}

ファイル名を作り出す例をもうひとつ. 何ヶ所かの調査地で,それぞれ4反復で,ときどき何かの調査を 行った結果がファイルにしまわれているという状況の例です.

@sites = ("forestA", "forestB", "openA", "openB", "gapA", "gapB", "gapC");
@years = ("1990", "1992", "1995", "1997", "2000");
$rep = 4;

foreach $site (@sites) {
    foreach $year (@years) {
        for ($r = 1; $r <= $rep; ++$r) {
            $source_file = sprintf ("%s%s_%1d.txt", $site, $year, $r);
            $target_file = sprintf ("stat_%s%s_%1d.txt", $site, $year, $r);
            system ("perl program3.pl $source_file > $target_file");
        }
    }
}

規則的に作り出せない調査地名や調査年ははじめに配列にしまって, あとは foreach で各要素を参照しています.

こうしたワザを使えば,多量のデータファイルの処理も怖くなくなります.



| Top Page | プログラミング | Perl 目次 | prev | next |