CGI-BBS > CGI > Perl > 1行だけの削除


カレッヂ
カレッヂ


質問者 tanuponn  投稿日 3/2(金) 18:21:17
CSVファイルの1件分のデータ(先頭)をある番号と一致したときにその行だけ削除したいのですが1行だけの削除とはどのように記述したら良いのでしょうか??

勉強不足で申し訳ありません。ご教授お願いします。

#ファイルの読み込み
open(IN,"$FILE_PASS");
while($FILE_STRING = <IN>){
  if ($FILE_STRING eq $C_DATA[0]){
       #ここで削除したいのです。#
  }
}
                
# ファイルを閉じます
close(IN);


---------------------------------------
CSVのデータ
54,name,year,month,day,time
     :
     :
     :
100,a_name,a_year,a_month,a_day,a_time
回答者 さくら  [削除]  投稿日 3/3(土) 03:55:57
>CSVファイルの1件分のデータ(先頭)をある番号と一致したときにその行だけ削除したいのですが1行だけの削除とはどのように記述したら良いのでしょうか??

番号と一致させるという事なら、$C_DATA[0]には、番号だけが入っているのですね。
それなら、読みこんだデータをスプリットする必要があります。

($index_num,$name,$year,$month,$day,$time) = split /,/, $FILE_STRING;

こうすると、$index_num に番号が入ります。 
次の行を読むには next を使います。

貴方のコードに追加するなら下記のようになります

open(IN,"$FILE_PASS");
while($FILE_STRING = <IN>){
  ($index_num,$name,$year,$month,$day,$time) = split /,/, $FILE_STRING;
  if ($index_num eq $C_DATA[0]){
       next;
  }
}

ここからは、余談です。
上記のコードでも良いのですが、
削除するデータも split するのは、効率的にも良くありません。
そこで、正規表現をつかって、最初に判断するのが良いと思います。
<IN> と書くだけで $_ にコピーされますので、$FILE_STRING は要りません。
そこで、こんなコードはどうでしょうか?

open IN, "$FILE_PASS";
while(<IN>){
 if (/^$C_DATA[0],/o){ next; }  #これで要らない行はnextされる
 #ここに必要な行の書きこみ用のコードを書く
}
close IN;

/^$C_DATA[0],/o の正規表現について

// はマッチ演算子です。これでパターンをかこみます。
^ は文字列の先頭を意味します。
$C_DATA[0] はマッチさせる文字列そのものです。
, は$C_DATAの後にカンマを要求します。
例えば$C_DATA[0] が 2 の場合、20や21にマッチしない様にカンマを要求します。
o はループ構造のなかでも、一度しか、正規表現をコンパイルしません。(効率が良い)

これで、読みこんだ行の先頭に$C_DATA[0]がある、行にマッチします。

以上、余談でした 失礼しました。
それではがんばって下さい。
回答者 羊相談員  [削除]  投稿日 3/3(土) 11:21:36
さくらさんの方法を試してみましたができませんでした。


#ファイルの読み込み
open(IN,"$FILE_PASS");
while($FILE_STRING = <IN>){
  if ($FILE_STRING eq $C_DATA[0]){
       #ここで削除したいのです。#
  }
}

↑この中に削除処理を追加することはできないと思います。
(もしかしたらできるかもしれませんがいろいろやってみてもダメでした)
ファイルを配列に読み込んで、削除したい行をspliceで削除して、ファイルに上書きする必要があります。
回答者 さくら  [削除]  投稿日 3/4(日) 03:01:32
>さくらさんの方法を試してみましたができませんでした。
>↑この中に削除処理を追加することはできないと思います。

羊相談員さんからご指摘がありましたので、一応レス致します。

私が、書いたコードは、if ブロックの中で next しているだけですよ。
if(条件式){ next; } こうなっています。
if ブロックを抜けてから、配列にpushするなりの処理を書く必要が有ります。
どんな処理コードを書くかは、やりたい事によるでしょう。
どちらにしても、if(条件式)で 真 になったものは、nextされますので、
ifブロックの下に来る事は有りません。

一応、読みこみから書きこみまでの完全なコードを書きます。
ファイル名は $filename に
データ番号は $data_num に
入っているものとします。
ファイルは読み書き両用でopen します。
seek と truncate も使っています。

-----------------------------------
open IN, "+<$filename" or die;
while(<IN>){
        if(/^$data_num,/){ next; }
        push @array, $_;
}
seek IN, 0, 0;
foreach(@array){ print IN $_; }
truncate IN, (tell IN) or die;
close IN;
------------------------------------

これで削除して書きこみもしました。
ここでは、配列をつかって、書きこみコードをwhileループの外に出しましたが、
もちろんwhileブロックの中に書きこみ用コードを書くこともできます。
テンポラリーファイルをopenしておき、元のファイルから読んで、
テンポラリーファイルに書きこみます。
while ループを抜けたら、元ファイルを unlink して テンポラリーファイルを rename します。
この方法のほうが、cgiで使う場合で、csvファイルが大きくなった場合には、
配列を使わないので、メモリーを節約でき、サーバ負荷が少なくてすみます。
必要なら、またコードを書きますので、レスして下さい。

-----------------
それと、ちなみに羊相談員さんのいうspliceでは、削除したい行が何行目か、
あるいは、配列の何番目の要素か知る必要があります。
データ番号と行番号(何行目かという事)が一致しているとは限りません。データが削除された場合には、
データ番号を振り直さなければなら無くなります。
そうしなければ、結局、読んだ行をカウントし、マッチや eq 演算子をつかって
削除したいデータの行番号を取得してからspliceする事になります。

質問者 tanuponn  [削除]  投稿日 3/5(月) 10:47:44
ありがとうございます!!
おかげで助かりました。

このページは終了したので返信(回答)は書きこめません

Web裏技