電子書籍の厳選無料作品が豊富!

ウィンドウズ環境でactive perlを利用しています。
あるディレクトリーの中に2000位のファイルが入っており、このファイルを読み込んで置換処理を行うため下記のようなperlを作っています。
1ファイルはおおよそ3000行くらいです。

foreach(<*.html>) {
open(IN_FILE, $_) || die;
open(OUT, ">$_.tmp") || die;
@record=<IN_FILE>;
foreach $record (@record){
$record =~s/aaa/bbb/isg;
}
print OUT @record;
close( IN_FILE);
close( OUT);
rename("$_.tmp", $_) ;
}
当たり前かもわかりませんが、メモリー不足になり処理が中断します。
ディレクトリーの中のファイル数を600程度に少なくすると処理が完了します。
そこで質問なのですが、ディレクトリーの中のファィルを一度に全部読み込まず、1ファイルずつ順次読み込んでいくことはできないでしょうか。
最初の1行目のforeachをwhileに変えたりしたのですがうまくいきません。
対応策あればご教示ください。

A 回答 (3件)

まず、<*.html>が原因かどうかの確認。


@files = <*.html> ;
だけのスクリプトが動作するかどうか。

動作しないようなら、ここでのメモリ不足なので、対策をする。
例えば、opendir, readdir, closedir を使う。
my $dirname = '.' ;
opendir my $dp, $dirname ;
while(my $fname = readdir $dp){
# 今回はカレントディレクトリなので不要だが
# readdirでは名前しか得られないので、実際にopenなどで使うには
# opendirに使ったディレクトリを追加する
my $fullname = $dirname . '/' . $fname
# html以外は対象外
if ( $fname !~ /.*\.html$/ ) {
next ;
}
...
$fname/$fullnameについての処理
...
}
closedir $dp;


動作するようなら、その後の処理が問題。
今のやり方(一時ファイルに出力→リネーム)でやるなら、@record=<IN_FILE>;などと全部取り込まなくても、一行ずつ処理すればいい。

@record=<IN_FILE>;
foreach $record (@record){
$record =~s/aaa/bbb/isg;
}
print OUT @record

while(my $record = <IN_FILE>){
$record =~ s/aaa/bbb/isg;
print OUT $record
}

この回答への補足

早速回答いただきありがとうございます。
@files = <*.html> ;は作動しました。
while(my $record = <IN_FILE>){
$record =~ s/aaa/bbb/isg;
print OUT $record;
}
もうまくいきました。本当に有難うございます。
実は多分「opendir, readdir, closedir 」を使うのだろうなと考えていたため、
一時ファイルに出力→リネームの場合に対応する方法があるとは考えてもいませんでした。
質問内容について聞けば全てに応用できると思っていたのですが、応用できないパターンが出てきてしまいました。
新規にファイルを作るパターンもよく利用しておりこちらもメモリー不足で悩んでいます。
こちらもディレクトリーの中に2000位のファイルが入っており、
1ファイルはおおよそ3000行くらいです。
このファィルから特定の情報を抜き出し、一つのファイル(下記の場合はichiran.html)として出力します。
元のファイル一つに対し抜き出した情報は1行しかありませんので出来上がったichiran.htmlは2000行程度のファイルです。
下記のようなperlの場合はどのようにすればいいのでしょうか。
追加で申し訳ありませんがよろしくお願いします。
foreach(<*.html>) {
open(IN_FILE, $_) ;
@record=<IN_FILE>;
foreach $item (@record){
#項目
if ($item=~ /.*<[^>]*Title\">([^>]*)<.*/si) {
$koumoku= "$1";
$item= "";
}
#原価
elsif ($item=~ /.*<[^>]*Price\">([^>]*)<.*/si) {
$kakaku="$1";
$item= "<tr><td>$koumoku<\/td><td>$kakaku<\/td><\/tr>
}
else {
$item="" ;
}
}
@total = (@total, @record);
}
close( IN_FILE);
$out_file="./ichiran.html";
open(OUT_FILE, ">>$out_file") ;
print OUT_FILE @total;
close( OUT_FILE);

補足日時:2012/05/13 13:20
    • good
    • 0

「元のファイル一つに対し抜き出した情報は1行しかありません」ということがわかっているなら, わざわざ巨大な配列を作らなくていいのでは?



もちろん「ファイルを一度に読み込む」ことが必要な処理だとも思えない.

push なんてのもあるけど.
    • good
    • 0
この回答へのお礼

何度もありがとうございます。
わかりにくい書き方で申し訳ありません。
個々のファイル内の抜き出すデーターは2行にわかれているものもあり、一旦全部読み込み整理しています。
ただしディレクトリーの中のファイルをすべて読み込む必要はなく、ここを何とかしたかったのですが、同じ物を何度も読み込んでしまいうまくいきませんでした。
教えていただいた内容を比べると
if ( $fname !~ /.*\.html$/ ) {
next ;
}
の部分が不足していました。
お陰様で、ディレクトリーの中のファイルを1つずつ読み込むことができるようになり、全て解決しました。
ありがとうございます。

お礼日時:2012/05/14 09:53

active perlのことはよくわかんないけど、ファイルないの置換するだけなら


perl -i.bak -pe"s/aaa/bbb/isgo;" *.html
とかじゃだめなのです?
    • good
    • 0
この回答へのお礼

早速回答いただきありがとうございます。
質問の書き方が悪くて申し訳ありません。
$record =~s/aaa/bbb/isg;
の部分は「なにか処理をする」という程度の意味で書いおり、実際には置換以外の処理もしています。

お礼日時:2012/05/14 09:57

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!