CGI-BBS > CGI > Perl > 配列データのファイルの圧縮


カレッヂ
カレッヂ


質問者 みこし  投稿日 2/16(金) 19:37:49
固定長の配列データをファイルに格納しているんですが、
配列の要素を削除してファイルに戻すと
出力データが少なくなった分ファイルに前のデータが
残ってしまっていることが判明しました。
このデータを消去してファイルを圧縮するには
どういう方法がよいのでしょうか。
回答者 羊相談員  [削除]  投稿日 2/16(金) 22:45:24
いろいろな意味に受け取れてしまうので
もう少し詳しく教えていただけないでしょうか。
例えばデータファイルの内容が
AAAAA
BBBBB
CCCCC
DDDDD
EEEEE
FFFFF
だったときにCCCCCの行を削除するとどうなるのでしょうか。
質問者 みこし  [削除]  投稿日 2/17(土) 07:33:36
すみません説明不足で
いただいた例に沿って説明すると

更新前のファイル   変更後(配列   更新後のファイル
(読み込み)                (出力)

AAAAA     AAAAA      AAAAA
BBBBB     BBBBB      BBBBB
CCCCC  →  DDDDD   →  DDDDD
DDDDD     EEEEE      EEEEE
EEEEE     FFFFF      FFFFF
FFFFF               (FFFFF)

という感じかと思います。
どうしても最後のデータのだぶりが確認されてしまうのです。
最初配列処理の誤りだと思ってダブる理由をさんざん考えたのですが、
やっとファイルの問題だと分かってきました。
この(FFFFF)の部分のファイルを圧縮したいのです
回答者 羊相談員  [削除]  投稿日 2/17(土) 09:08:51
上書きするため最終行が消えないということでしたら
open(FH,"+>file")
ファイルに書き込むときにこのモード(+>)で開くと
何もしなくてもファイルの中身が空になります。
回答者 羊相談員  [削除]  投稿日 2/17(土) 09:21:55
それと、この書き込みモードで動作確認をするときは
ちょっと間違えただけでデータが全部消えてしまうことがあるので
バックアップをととってから動作確認することをお勧めします。
回答者 さくら  [削除]  投稿日 2/18(日) 00:18:05
>この(FFFFF)の部分のファイルを圧縮したいのです

要するに、(FFFFF)の部分を切り詰めれば良いのですか?
それなら、ファイルへ書きこむとき、print文の後に下記のコードを一行追加すればOKです。

truncate (FH, tell(FH));

truncate は、ファイルを切り詰めます。引数にファイルハンドルとファイルポインタの位置を
指定します。
print が終わった時点では、最後にprint した行の行末にありますので、
この位置を取得するには、tellを使います、引数はファイルハンドルです。

質問者 みこし  [削除]  投稿日 2/18(日) 08:21:28
羊相談員さん、重ねてのご回答ありがとうございます。
私も最初 open(FH,"+>file") で
ファイル更新をしてみたのですが上書きがうまくできなかったので
null ファイルになってしまっていたのです。
そこで open(FH,"+<file") に変えてみたところ
上記のような結果になりました。
そうこう悩んで行き詰まってみなさんのご厚意に
甘えてみようと考えた次第です。
質問者 みこし  [削除]  投稿日 2/18(日) 08:32:43
さくらさん
大変参考になるご教授ありがとうございます。
で、早速試させていただきました。
結果、うまくいかなかったのでがっかりしたのですが、
解析をしてみようと tell(FH) をprintしてみました。
するとなぜか -1 が返されていました。
これでは確かに切り詰めができません。
ここがちょっと悩みどころです。

ただ試しに実際のデータ長を引数として入れたところ
期待した結果が出ましたのでとりあえず少々アレンジしてみようかと
考えています。
回答者 羊相談員  [削除]  投稿日 2/18(日) 12:20:25
最後の行を削除する方法
open(FH, "+<data.csv");
while(<FH>){
    $p = tell(FH) if (not eof(FH)
}
truncate(FH,$p);

どういう仕組みで動いているかはわかりません。
回答者 さくら  [削除]  投稿日 2/19(月) 02:10:49
貴方の使っているシステムでは、本当に tell が使えないのですか?
もし tell が使えないなら、羊相談員さんの方法も使えない事になります。
羊相談員さんの方法は、ループするたびに、eofを使ってファイルの終わりかどうかを調べている、not 演算子を使っているので、ファイルの終わりでなければ、条件式が真になる。
そして、tell によって現在のポインタの位置を $p に代入している。
これをループする度に繰り返し、ループを抜けた時点の $p の位置を引数にして、
truncate を行なっていますので tell が使えないとすればこの方法も使えない事になります。

tellが使えるかどうか試しにテスト用のスクリプトを書いてみてはいかがでしょうか。
例えば、
1.  任意のファイルを開く
2.  seek でポインタの位置を先頭にセットする。
3.  tell をプリントして見る。
4.  seek でポインタの位置を変えてみる。
5.  tell でプリントして見る。
こんな感じでどうでしょうか?一応サンプルのコードも書いておきます。

open FH, filename;
seek FH, 0, 0;        #ポインタをファイルの先頭にセット
print (tell FH);      #0を表示すればOK
seek FH, 1, 0;        #ポインタを1バイト進める
print (tell FH);      #1を表示すればOK
close FH;

これで、もしtellが使えない事が解れば貴方のおっしゃる通り、
固定長のデータの特徴をいかしてポインタの位置を正確に計算するしかないでしょう。

がんばって下さい。


返信(回答)する


Web裏技