プロが教えるわが家の防犯対策術!

ActivePerl5.8.4にて、以下の規則に従い、テキストファイルから複数行を削除しようとしています。

・1つの行はアルファベットと数字から成っており、アルファベットはA,B,Cの順に出現する。A,B,Cの行で一かたまり。
・数字部分が全く同じかたまりが出現すると、後で出現したかたまりを削除する。
・数字部分が全く同じかたまりが出現するときは、必ず連続で出現する。
下のような感じです。(スペースの都合で「\n」をつけましたが、実際には改行までが1行に書かれているテキストファイルを読み込みます。)

【入力】
A 1\nB 2\nC 3\nA 1\nB 2\nC 3\nA 1\nB 2\nC 2[EOF]
【出力】
A 1\nB 2\nC 3\nA 1\nB 2\nC 2[EOF]

すぐに思いついた方法はこんな感じです(まだ勉強不足のため、コードではあらわせません)。

 1.ファイルの1行ずつ読み込む
 2.A,B,Cをキーにして数字をハッシュに格納
 3.2回目のCまで読み込んだところで1つ前のハッシュと比較
 4.AからCの数字がすべて同じだった場合に、現在の行(2つめのCを読み込んでいる)とその前2行を削除(実際にはnextでprintを飛ばす)

ところが、4の、さかのぼって行を削除する方法が思いつきません。
なにぶんperlの勉強を始めたばかりで突っ込み所満載のロジックだとは思いますが、解決策をぜひご教示ください。

A 回答 (4件)

テキストファイルというのは、途中を更新したり削除したりするものじゃないんです。


どのような経緯で、そのような厄介なことになってしまったのかわかりませんが、
テキストファイルを無理に使おうとした弊害だと思います。
テキストファイルを全部読んで、処理をし、そして書き戻すのが簡単だと思います。

D:>test1.pl
step1: $rec = 'A 1,B 2,C 3,A 1,B 2,C 3,A 1,B 2,C 2,A 3,B 3,C  3,A 3,B 3,C 3,A 3,B
3,C 4,'
step2: $rec = 'A 1,B 2,C 3,A 1,B 2,C 2,A 3,B 3,C 3,A 3,B 3,C 4,'
--out--
A 1
B 2
C 3
A 1
B 2
C 2
A 3
B 3
C 3
A 3
B 3
C 4
D:>

#!Perl
use strict;

#【入力】
my @rec = <DATA>;
chomp @rec;
my $rec = join(',' => @rec) . ",";
print "step1: \$rec = '$rec'\n";

# データが正しいかチェック
die "data in disorder!\n" unless $rec =~ /^(A \d+,B \d+,C \d+,)+$/;

# データを処理, 連続するデータはひとつに

$rec =~ s/(A \d+,B \d+,C \d+,)\1+/$1/g;

print "step2: \$rec = '$rec'\n";

#【出力】

$rec =~ s/,/\n/g;
chomp $rec; # 最後の,を削除

print "--out--\n";
print $rec;

__DATA__
A 1
B 2
C 3
A 1
B 2
C 3
A 1
B 2
C 2
A 3
B 3
C 3
A 3
B 3
C 3
A 3
B 3
C 4
    • good
    • 0
この回答へのお礼

ご回答をありがとうございました。この手の仕様が厄介だということすら、認識できていませんでした。ご回答の例は、大変わかりやすくてためになりました。活用させていただこうと思います。

お礼日時:2007/02/22 22:16

出力データをメモリ上にもつ以外の、


「さかのぼって行を削除する方法」に対する他の答えとして・・
一旦、別ファイルに出力しておいて、
最後に別ファイルを元のファイル名にrenameする、
というのも分かりやすいです。
「A~Cの数字が一つでも違った場合、
 ハッシュ(一つ前ではない方)から出力ファイルへ書き出す」
という感じですね。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。こういうてもありますね。大変参考になりました。

お礼日時:2007/02/22 22:18

3行ずつ読み込んで、前の3行と比較し出力



$bef = ""; # 前3行
$line= ""; # 現3行
$ans = ""; # 出力用
open(IN,"test.txt");
while(1){
# 3行読み込み$lineに入れる
$line = <IN>;
$line .= <IN>;
$line .= <IN>;
if( $line eq "" ){ last; } # 読み込めなければ終了
elsif( $bef eq $line ){ next; } # 前3行と同じなら次の3行へ
else {
$bef = $line; #前3行に現3行を入れる
$ans .= $line; #出力用に現3行を入れる
}
}
close(IN);
# 出力
open(OUT,">test.txt");
print OUT $ans;
close(OUT);

別処理で元ファイルが書き換わる場合は、
排他処理を行わないといけないですね。

意味を判りやすく書こうとするとこんな感じですね。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。複数行の読み込み例を示していただいて、とても参考になりました。

お礼日時:2007/02/22 22:17

3行ずつ読み込んだ方が簡単に出来ると思いますよ。

    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。3行ずつ読み込めるのですね。もっと勉強いたします。

お礼日時:2007/02/22 22:14

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