• ベストアンサー

要素を削除する最適な方法

たいへん困っています。Perlでプログラムを作成しており配列要素の削除を行なおうとしています。 例えば下のようなデーターをdata.txtファイルに保存しているとしてください。 1,洋服,婦人服,子供服,男性服, 2,時計, 3,アクセサリ, 上のデーターで1行目にある子供服を削除して 1,洋服,婦人服,男性服, 2,時計, 3,アクセサリ, のようにずらしたいのです。 私がとった方法はデーターをopenで開きforeachで行をまず呼びこみます。 if(!open(DATA,"$data")){&error('ファイル読み取りエラー。'); } else{ @data=<DATA>; close(DATA); } foreach $line (@data){ chomp $line; ($no,@sub)=split(/,/,$line); if($no eq 1){ $i=-1; foreach (@sub){$i++; if($sub[$i] eq '子供服'){next;} else{push @newsub,"$sub[$i]";next; } } push @newline,"$no,@newsub\n";} else{push @newcline,"$line\n";} } これで@newlineを表示すると 1,洋服,婦人服, 男性服, 2,時計, 3,アクセサリ, のように男性の前の部分が半角空白として保存されます。 どうしてなのでしょうか?そもそもこういったやり方が間違っているのか、それとも一部だけまちがっているのかもわかりません。お手数ですがどなたか教えてください。

  • Perl
  • 回答数4
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
  • damejan
  • ベストアンサー率30% (58/192)
回答No.1

簡単なことを複雑にやっている印象があります。本当は、bakusuiさんにとって良くないことだと思いますが、私だったら、こうするという例を示します。 $datfile = @ARGV[0]; open(DATA, $datfile) || die "can't open $datfile\n"; while($line = <DATA>){ ($num, @items) = split(/,/, $line); if($num == 1){ $line =~ s/子供服\s*,//g; } print $line; }

その他の回答 (3)

  • damejan
  • ベストアンサー率30% (58/192)
回答No.4

>1行をforeachで探し出してpushで振り分けるようにしていました。 残したいデータもしくは削除したいデータを振り分けて個別に後で処理したいのなら、そうすべきでしょう。単にファイルに書き分けるだけなら、配列に入れずに、そのままファイルに書き出せばよいのではないでしょうか。 >あと、$line =~ s/子供服\s*,//g; の\s*は必要なのでしょうか? もちろん、なくても構いません。単にデータ中に余計なスペースがあった場合の予防です。自分で作成したデータと言えど信用しません(余計なスペースがどこに入っているか分からないですから)。 以下のように、まず最初に余分なスペースを消去した方がいいですね。 $line =~ s/[ \t]+//g; #余計なスペース、タブの消去 $line =~ s/子供服,//g;

bakusui
質問者

お礼

ありがとうございました。本当に助かりました。 感謝致します。 勉強にもなりました。 失礼します。

  • tancoro
  • ベストアンサー率52% (11/21)
回答No.3

#1さんが言っておられる通り、複雑なことをやりすぎている気がします。(文字列の置換で十分対応できる。) どうしても、配列の要素から特定の条件に当てはまるものを削除したいというのであれば、grep等を使うのが一番スマートな気がします。 今の場合であれば、 @sub = grep { $_ ne '子供服' } @sub; 等は、いかがでしょうか?

  • damejan
  • ベストアンサー率30% (58/192)
回答No.2

補足します。 > 簡単なことを複雑にやっている印象があります。 文字列のレベルで処理できることに対して、わざわざ配列を使っているという意味です。文字列のレベルで処理できることは、文字列の置換や消去で行った方が良いでしょう(プログラムが簡単)。 今回の質問の例では、わざわざ配列に入れ直す必要もないと思います。もちろん、処理内容(例えば、行内の項目をソートしたりする場合)によっては、配列に入れ直した方が良いでしょう。

bakusui
質問者

お礼

結論からいいますと、うまくいきました。 感動しました。一度頭の中で配列を使うと思いこんでしまうとそればっかりしてました。 たいへん感謝いたします。 いままでの傾向(私自身的に)では例えば掲示板のかきこみなどを削除をするとき1行をforeachで探し出して pushで振り分けるようにしていました。1行まるごと削除する場合はこれでいいのですよね? 文字列レベルの時おっしゃっていたようにすればいいんでしょうか? あと、$line =~ s/子供服\s*,//g; の\s*は必要なのでしょうか? これを付加しなくてもうまくいってるような気がするのですが・・・\s\は0個以上空白文字ですよね?あったほうがいいのですか?

関連するQ&A

  • foreachの制御

    ファイルを編集するのにforeachで@allbodyを1行ずつ調べているとします。  foreach $line (@allbody){ $iを含む行があれば$tmpと入れ替えてファイルへ保存   if ($line =~ /$i/){    $line =~ s/.*/$tmp;    print FILE @allbody;   } でも$iを含む行がなければ、新しく追加書き込みをしたい で、上に続けて    else {     push (@allbody,$tmp);     print FILE @allbody;    }   } とやると、おかしい事なりますよねぇ どうすればいいのでしょう?

    • ベストアンサー
    • Perl
  • 00月00日00時00分→0000年00月00日00時00分

    すみません。 これを、西暦年表示を加えたものにしたいのですが、どうすればよかったでしょうか…。 見本:  2007年01月04日12時59分 *-*-*-*-*-*-*-* sub time_get { @ts = localtime(time); # $ts[4]月:$ts[3]日:$ts[2]時: $ts[1]分 $tm = time; $ts[4]++; $ts[5] = $ts[5] +1900; if ($ts[1] < 10) { $ts[1] = "0$ts[1]"; } if ($ts[2] < 10) { $ts[2] = "0$ts[2]"; } if ($ts[3] < 10) { $ts[3] = "0$ts[3]"; } if ($ts[4] < 10) { $ts[4] = "0$ts[4]"; } $ad = $ENV{'REMOTE_ADDR'}; $ip = $ad . gethostbyaddr(pack("C4",split(/\./,$ad)),2); *-*-*-*-*-*-*-* sub rank_in { $file_name = "list1.txt"; &file_read; $now_tm = "$ts[4]/$ts[3] $ts[2]:$ts[1]"; foreach $line (@data) { ($k,$i,$name,$ips,$j) = split(/,/,$line); if ($ip eq $ips || $name eq $FORM{name}) { $line =""; if( $gt > $k ) { $gt=$k; $now_tm = $i; } } push(@rank,$line); } @data = @rank; $t=$FORM{total}; $line = substr( " $t" , length $t, 10); if($FORM{home} eq 'http://'){$FORM{home} ='';} $line = "$line,$now_tm,$FORM{name},$FORM{comt},$FORM{home},$ip,\n"; push(@data,$line); push(@data,"end"); @data = sort(@data); @rank =(); $i=0; foreach $line (@data) { if (++$i == $rank_save || $line eq "end" ) { last; } push(@rank,$line); } @data = @rank; &write_file; }

    • ベストアンサー
    • CGI
  • perlでファイルのデータの一部を削除したい

    久しぶりにPerlを操作してファイルのデータの一部を削除したいのですが、削除出来ずに困っております。 3年以上触っていないため、過去のファイル等々を見ながらやってましたが、結果は出来ずじまいで停滞して困っております。 ご指導いただけないかと思い、書き込みしました。 ファイル名 $tmpfile データ構成  no,名称 1<>フレーム 2<>レンズ 3<>カメラ 4<>ファインダー 5<>めがね 6<>ズーム これで「めがね」を削除したいのです。 データnoで削除したいと思っておりますが、うまく動作しません。 以下ソース open(DB,"$tmpfile") || &error("Open Error : $tmpfile"); flock(DB, 1); @lines = <DB>; @new=(); foreach $line (@lines) { $flag=0; ($no,$meisyo) = split("<>", $line); foreach $x (@DEL) { if ("$x" eq $tmpnum) { $flag=1; last; } } if ($flag == 0) { push(@new,$line); } } open(OUT,">$tmpfile") || &error("Write Error : $tmpfile"); print OUT @new; close(OUT); ここまで 上のソースですと、エラーこそならずに(そうみえているだけかもしれません)終わりますが、データ削除が正しく出来ませんでした。 説明不足があるかもしれませんので、指摘いただけますと幸いです。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • perlでの三次元配列の作り方

    perlで三次元配列をテキスト入力から作りたいのですが、例えば二次元配列の場合 foreach $line (@input) push @data, [split /[:]/, $line]; で@dataが二次元配列になるのですが、三次元配列の場合このあとに push @output, \@data; とするとリファレンスが同じであるためループをまわしてもうまく三次元になりませんよね。 解決法はありますでしょうか?

  • CSVに追加データを挿入したいです。

    この前質問したばかりなのですが、別の質問させてください。 数日頑張ったのですがどうしても出来なくて… やりたいこと ↓のようなものが書かれてあるCSVファイルがあります。 服 トップス 服 ボトムス 服 ブランド このCSVファイルに、 服 トップス なら01&01_01 服 ボトムス なら01&01_02 服 ブランド なら01&01_03 という情報を1列目に付け足したいです。 そこで、以下の配列を作り、 $data array("01&01_01" => "トップス", "01&01_02" => "ボトムス",   "01&01_03" => "ブランド") csvファイルの、「服 トップス」をexplodeで分けました。 $log = "list.csv"; # ログ名 $lines = file("$log"); foreach ($lines as $l) {  $line = explode(",",$l);  $push = $line[0].",".$line[1].",".$line[2].",".$line[3].",\n";  $cate = explode(" ",$line[2]); # $line[2]は、上記の「服 トップス」というデータを挿入しています。  $catea = $cate[1]; # サブカテゴリ名を格納 $cate[1]=トップス  foreach($data as $key => $value){   if($value == $catea){    $LOG[$catea] .= $key.",".$push."\n"; # カテゴリ情報を追加したcsvデータを1行ずつ保存   } } foreach($LOG as $keys => $values){ $file = fopen("list2.csv", 'w'); # カテゴリ情報を追加したcsvデータを書き込み fwrite($file, $values); fclose($file); } すると、foreach($LOG as $keys => $values){ の部分でforeachエラーが出ます。 根本的に記述が間違っているのでしょうか。 お手数かけますが、ご指南よろしくお願いします<(_ _)>

    • ベストアンサー
    • PHP
  • 親記事を削除するとレスも削除

    いつも参考にさせてもらっています。 タイトルどおりなのですが、 親記事を削除すると親Noを含むレスをすべて削除できるように $lines = file('res.log'); for ($i = 0; $i < count($lines) + 1; $i++) { $items = explode("\t", $lines[$i]); if ($items[1] == $_GET['oya_del']) { $dno = $i; $dnum2++; if ($dnum) array_splice($lines $dno, $dnum2); $fp = fopen('res.txt', 'w'); foreach($lines as $line) fputs($fp, $line); fclose($fp); } } と、記述しています。 しかしレスが2つ以上あると、1つ残ってしまいます。 どのようにしたらよいのか教えていただけないでしょうか。 お願いいたします。

    • ベストアンサー
    • PHP
  • ログを複数同時に削除

    こんにちは。 Perlでログを複数同時に削除する方法が分かりません。 ログファイル「log.txt」に Num=1234<>タイトル=あいうえお Num=1235<>タイトル=かきくけこ Num=1236<>タイトル=さしすせそ Num=1237<>タイトル=さしすせそ のようなデータが入っていて フォームのチェックボックス(name=delck)から送られてきた値と マッチするデータを削除したいと思っています。 例:1234と1236 最初はラジオボタンにして1行だけ削除できるようにしたのですが やはり複数同時に削除できた方が便利なので 試しているのですが、どう書けば良いのかわからない状態です。 複数の値が送られてきた場合は @delckに入るようになっています。($delck[0]が1234、$delck[2]が1236) ログの各行を、delckの数だけチェックすれば良いのかなと思って わからないなりに↓こんな感じで、 foreachの中にforeachを入れたりしてみたのですが foreach $data (@log){ foreach $del (@delck){ $ck = "Num=$del<>"; if($data =~ /$del/){$match = 1;} else{push(@newdata,$data);} } } これだと1個目のdelckにマッチしても、2個目のdelckにはマッチしないから結局@newdataに入ってしまうようで 思った通りの動作をしてくれませんでした。 他の書き方をしてみても、結局発想は↑と同じになってしまって 結果は同じになってしまいます。 どなたか詳しい方、アドバイスいただけると助かります! よろしくお願いします。

    • ベストアンサー
    • Perl
  • 大文字と小文字を区別しない検索にする方法

    勉強する間もなく手探りでフリーCGIの改造をさせて頂いています。 if ゃ foreach などは分かりやすいですが、変数の意味が良く理解できていません。 どうぞお力添えをお願い致します。 現在、フリーのCGIでデータ登録と検索をしていますが、大文字と小文字の 区別を無くしたものにするにはどうすればよいでしょうか? 1) AbcDe も abcde も同じにしたい。 2) 可能なら全角と半角も区別無しにしたい。   ただ、2の方は漢字も使うので難しいでしようか? iオプションを使えとの記述を見ましたが、どこを変えるのか解りません。 ソースを載せますので、ご指導宜しくお願い致します。 # 入力内容を整理 $in{'word'} =~ s/\x81\x40/ /g; @wd = split(/\s+/, $in{'word'}); @find=(); $i=0; # 検索処理 foreach (@line) { $flag = 0; foreach $wd (@wd) { if (index($_,$wd) >= 0) { $flag = 1; if ($in{'cond'} eq 'OR') { last; } } else { $andor="and 検索"; if ($in{'cond'} eq 'AND') { $flag=0; last; } $andor="or 検索"; } } # ヒットした場合 $view = $in{'viewc'}; if ( $view eq '' ) { $view = $views } if ($flag) { $i++; next if ($i < $page + 1); next if ($i > $page + $view); push(@find,$_); } } # 検索終了

    • ベストアンサー
    • CGI
  • テキストデータから指定行の削除

    phpでサイトを作っています。 テキストデータを一行ずつ表示させ、指定した行を削除したいのですが、どうすればいいのでしょうか? -- data.txt -- aaa,data1, bbb,data1, ccc,data1, ddd,data1, -- php -- <?php $del=$_POST['del_no']; if($del!=""){ $data_all = file("data.txt"); for($i=0;$i<count($data_all);$i++){ $data_array = explode(",",$data_all[$i]); if($data_array[0]!=$del){//削除データ以外を再書き込み $txt="$data_array[0],$data_array[1],\n"; $fp = fopen("data.txt", "w"); flock($fp,1); foreach ($txt as $line) {// 1行ずつファイルへ書き込み? fwrite($fp, $line); } fclose($fp); break; } } } ?> <b>data list</b> <? $data_all = file("data.txt"); for($i=0;$i<count($data_all);$i++){ $data_array = explode(",",$data_all[$i]); echo "$data_array [0]<br />"; echo "<form method=\"post\">"; <input type=\"hidden\"name=\"del_no\" value=\"$data_array[0]\" />";//削除するデータをPOST echo "<input type=\"submit\" value=\"削除\" />"; echo "</form>"; } ?> こうすると、テキストデータが白紙になってしまいます。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • PHPのファイルの読み方のコード

    下記はpearlのファイルの読み込みです #!/usr/local/bin/perl print "Content-type: text/html\n"; print "\n"; require './jcode.pl'; if (!open(IN,"$userfile")) { &error("投稿データファイルを開けません"); } @DATA = <IN>; @DATA = reverse(@DATA); close(IN); foreach $line (@DATA){ ($f1,$f2,$f3) = split(/,/, $line); &jcode'convert(*line,'sjis'); push(@CS,$line); } foreach (1.. 100) { ($f1,$f2,$f3) = split(/,/, $CS[$_]); print "$f1\n"; } exit; ############################################## foreach (1.. 100) { ($f1,$f2,$f3) = split(/,/, $CS[$_]); print "$f1\n"; } この部分のPHPのコ-ド書き方をお願いします

    • ベストアンサー
    • PHP