Perlについて質問です。


ファイルの一部を変更して同じファイル名で
保存する方法を教えてください。

ファイルを全て読み込んで配列にいれ、
配列の一部を修正し、再び配列から
ファイルへ出力するのが一般的でしょうか?

それとも何か変更位置をシークして
変更する方法があるのでしょうか?

簡単な例と解説を明記して
いただければと思います。

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:2006/02/09 17:54:19
  • 終了:--

回答(5件)

id:ootatmt No.1

ootatmt回答回数1307ベストアンサー獲得回数652006/02/09 18:03:10

ポイント20pt

ロック開始


繰り返し(ファイル終了まで) {

 ファイルを一行ずつ読み込む

 修正箇所があれば修正

 一時ファイルに追加しながら書き出す

}


元のファイルを削除し、

一時ファイルをリネーム


ロック解除


こんな感じでどうでしょうか?

ファイルが大きくてもメモリーの使用量を最小限に抑えることが出来ると思います。

id:ganessa

ありがとうございます。

すみませんが、それをPerlで

記述していただけますでしょうか?

2006/02/09 18:06:11
id:rcconf No.2

rcconf回答回数2ベストアンサー獲得回数02006/02/09 18:28:54

ポイント20pt

コマンドが使用できるのであれば、

ファイル内の文字列「foo」を「bar」に変換する場合


% perl -pi.bak -e ’s/foo/bar/g’ file


で、元のファイルを file.bak という名前で保存し、file を直接書き換えることができます。。


% perl -pi.bak -e ’s/foo/bar/g’ *.html


と複数のファイルを指定することもできます。

id:ganessa

なるほど。

念のためコマンドを使用しないパターンも

お教えいただければと思います。

2006/02/09 19:10:43
id:andi No.3

andi回答回数448ベストアンサー獲得回数02006/02/09 19:53:07

ポイント20pt

簡単な例と解説を付けて書いてみました。


例①テンポラリファイルを利用する方法

# 標準モジュールをインポート

use FileHandle;

use File::Copy;


my($fh1,$fh2);


# 元ファイルを$fh1(読込専用)、テンポラリファイルを$fh2(書込専用)で開く

$fh1 = FileHandle->new(’file.txt’,O_RDONLY);

$fh2 = FileHandle->new(’file.tmp’,O_CREAT|O_WRONLY|O_TRUNC);


# 元ファイルを1行ずつ読み込み

while(my $line = <$fh1>){

# 元ファイルに”foo”を見つけると”bar”に修正

$line = s!foo!bar!g;

# テンポラリファイルに1行書込み

$fh2->print($line);

}


$fh2->close;

$fh1->close;


# テンポラリファイルを元ファイルに上書き

move(’file.tmp’,’file.txt’);


例②ファイルを読書きモードで開く方法


# 標準モジュールをインポート

use FileHandle;

use Fcntl qw(SEEK_SET);


my($fh,$buffer);


# 元ファイルを$fh(読書両用)で開く

$fh = FileHandle->new(’file.txt’,O_RDWR);


# ファイルの内容を全て取り込む

$fh->read($buffer,-s $fh);


# ”foo”を見つけると”bar”に修正

$buffer =~ s!foo!bar!g;


# ファイルポインタを先頭に戻す

$fh->seek(0,SEEK_SET);


# 修正した内容を上書き

$fh->print($buffer);


# 書き出した内容よりも後の値をクリア

$fh->truncate($fh->tell);


$fh->close;

id:ganessa

詳細な説明ありがとうございます。

知らなかったモジュールを使われてますね。

早速調べてみます。

同時に二つあけるとは考え付きませんでした。

参考にさせていただきます。

2006/02/09 23:02:51
id:noboru No.4

noboru回答回数94ベストアンサー獲得回数02006/02/09 20:01:40

ポイント20pt

一般的なテキストファイルの場合はご想像の通り、全部読んで変更して全部書く方法が一般的です。なぜかというとテキストファイルは途中の行の長さが変わる可能性があるからです。(もちろん自分で変わらないように作るというのであれば問題ありませんが)。


固定長を保証するファイルの場合は(内容がテキストであろうとバイナリであろうと) seek() を使って先頭位置まで移動して読んだり書いたりができます。但しOSによっては binmode でバイナリファイルであるということを明示する必要があります(UNIX系OSでは普通はこの必要はありませんが、use encoding でエンコーディングを指定している場合はやっておいた方がいいです)。


たとえばバイナリファイルでパックした4バイト整数を読み書きする場合、4バイトというのは固定なのでいきなり10番目の値の読み書きができます。


例: 読む場合

open(F, ”< $filename”) or die;

binmode F;

seek(F, 9 * 4, 0); # 1つのデータが4バイトなので4倍する。

read(F, $buf, 4);

close(F);

$n = unpack(’L’, $buf); # バイナリから数値に戻す。

id:ganessa

なるほど。

わかりやすい説明ありがとうございます。

こちらの方法でも挑戦して見ます。

2006/02/09 23:04:33
id:kimbara No.5

kimbara回答回数638ベストアンサー獲得回数132006/02/10 07:53:10

ポイント20pt

そのファイルの利用状況によりますが、簡単な方法なら、

undef $/;

if (!open(FILE, ”PathToTheFile”) {

 my $data = <FILE>;

 close (FILE);

 {読み込んだdataの内容更新。例えば、 $data = ~s/ABC/XYZ/;などで通常の置き換え処理をする}

 if (!open (FILE, ”>PathToTheFile”) {

  print FILE $data;

  close (FILE);

 }

}


一気に読んで、文字列として更新して、一気に書き込むという手法です。そのファイルが共有されていて同時アクセスが考えられるなら、排他処理が別途必要です。

id:ganessa

ありがとうございます!

こちらにロック処理を加えて作ってみたいと思います。

みなさんお忙しいところありがとうございました。

2006/02/10 13:22:59

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません