人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

Perlについて質問です。

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

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

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

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

●質問者: ganessa
●カテゴリ:ウェブ制作
✍キーワード:Perl シーク ファイル 配列
○ 状態 :終了
└ 回答数 : 5/5件

▽最新の回答へ

1 ● ootatmt
●20ポイント

ロック開始


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

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

修正箇所があれば修正

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

}


元のファイルを削除し、

一時ファイルをリネーム


ロック解除


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

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

◎質問者からの返答

ありがとうございます。

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

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


2 ● rcconf
●20ポイント

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

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


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


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


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


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

◎質問者からの返答

なるほど。

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

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


3 ● andi
●20ポイント

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


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

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

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;

◎質問者からの返答

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

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

早速調べてみます。

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

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


4 ● noboru
●20ポイント

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


固定長を保証するファイルの場合は(内容がテキストであろうとバイナリであろうと) 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); # バイナリから数値に戻す。

◎質問者からの返答

なるほど。

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

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


5 ● kimbara
●20ポイント

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

undef $/;

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

my $data = <FILE>;

close (FILE);

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

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

print FILE $data;

close (FILE);

}

}


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

◎質問者からの返答

ありがとうございます!

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

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

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ