• ベストアンサー

ファイルの書き込み2

またまた失礼します。 やりたいことは以下です。 1.ディレクトリを開く。 2.ファイル達を読み込み。 3.追記用のデータファイルを開く。 4.「2」にデータを追記 5.別のディレクトリに書き出し。 以上です。 4番がうまくいきません。 ためしに4の工程を抜かすと問題なく書き出されます。 4の工程でやりたいことは、 1.タブ区切りのテキストファイルを読み込み。 2.書き出し用に読み込んだ(追記したいファイル)<productのpath="[ココの値!]"を取得。 3.「2」番で取得した値と「1」番で取得した値を比較。 4.「3」がtrueの行を追記して書き出し。 以上。 要するになにがしたいかというと、 仮にタブ区切りテキストには10行分のデータがあるとします。 それぞれの行には3つ分の値が入っており、先頭にIDが付いています。 一番最初に読み込んだファイル(追記したいファイル)にも同様にIDが振られており(<product path="ココ")そのIDとタブ区切りテキストのIDが一致した行だけ追記したいということです。 下記のコードだとなぜか、 10行分のデータがすべて追記されてしまいます。 コードは以下です。 #!/usr/bin/perl #既存ファイル読み込み&追記 $n_dir = "newXml/"; $b_dir = "xml/"; opendir(DIR, $b_dir); while($file = readdir(DIR)){ $bfile="$b_dir$file"; $nfile="$n_dir$file"; $dfile="data/data.txt"; if (-T $bfile) { open(IN, $bfile);#既存ファイルオープン @list = <IN>; close(IN); open(OUT,">$nfile");#書き出しファイルオープン foreach $line (@list) { if ($line =~ /\<product/){ $line =~ /path=\".*\"/;#path取得 $1; } if ($line =~ /\<\/product\>/) { open(IN, $dfile);#追加データファイルオープン while($data = <IN>){ chomp(@data = split(/\t/,$data)); $data[0] =~ s/\//_/g; if($data[0] == $1){#取得したパスとdata.txtとってきた値を比較 print OUT <<EOF; <ds path="$data[0]">$data[1]</ds> <kw>$data[2]</kw> $line EOF } else{next;} } close(IN) }else{print OUT $line;} } close(OUT); } else{next;} } closedir(DIR); 長々と申し訳ありません。 エラーなどは特にありません。 ご協力お願いします。

  • Perl
  • 回答数3
  • ありがとう数3

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

  • ベストアンサー
回答No.2

またまた失礼。。 $data[0] と $1 の中身が空でないかチェックしても駄目ですか? if($data[0] ne ""){~ >if($data[0] == $1){#取得したパスとdata.txtとってきた値を比較 の==をeqに変えてみても駄目ですか? (==は、数値でない場合や文字列として認識されてしまった数字の場合、正しく比較できないことがあります。  経験上、eqなら間違いなく比較してくれます) ご質問の件とはまったく無関係ですが、 >open(IN, $dfile);#追加データファイルオープン から >close(IN); までの間に処理をたくさん行なうと、その隙に外部からファイルアクセスがあったときに、ファイルを破壊されることがあります。 あらかじめ書き出す変数や配列を用意しておき、openからcloseまでを最短にするようにした方が安全ですよ。 前回私が書いたスクリプトが見本です。(恥)

trfnc223
質問者

お礼

またまたありがとうございます!! 空のチェックをしたら$1に値が入ってなかったです。 ただ、=をeqに変えたらちゃんとでなくなりました! つまり$1に値が入っていないので当然falseなので、 値が出ないという正しい処理になりました。 あとは$1にデータさえ入れば・・・ >までの間に処理をたくさん行なうと、その隙に外部からファイルアクセスがあったときに、ファイルを破壊されることがあります。 わざわざご指摘ありがとうございます! とても参考になりました!!!

その他の回答 (2)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.3

たぶん if ($line =~ /\<product/){ $line =~ /path=\".*\"/;#path取得 $1; } ここで$1に何か入っていると思っていて、さらに if($data[0] == $1){#取得したパスとdata.txtとってきた値を比較 ここでそれを参照しているつもりなんだろうけど、それじゃだめです。 まず$1とか$2とかで取り出すには、 $line =~ /path=(".*")/; のように、キャプチャのためのカッコを書かないといけません。 つぎに、仮にここでキャプチャしていたとしても if ($line =~ /\<\/product\>/) { こことか $data[0] =~ s/\//_/g; ここで正規表現を使った操作をしてしまっているから、ぶっ壊されます。 $<*digits*> Contains the subpattern from the corresponding set of capturing parentheses from the last pattern match, not counting patterns matched in nested blocks that have been exited already. (Mnemonic: like \digits.) These variables are all read-only and dynamically scoped to the current BLOCK. データファイルの読み込みは一回だけやれば十分だと思うけど もう寝るので指摘だけ :)

trfnc223
質問者

お礼

できました!!!! $line =~ /path="(.[^\"]*)"/;#path取得 $id = $1; ~~略~~ if($data[0] eq $id){#取得したパスとdata.txtとってきた値を比較 こうしたらちゃんと比較されました! ありがとうございます!

  • Ceren
  • ベストアンサー率49% (90/183)
回答No.1

> 4.「2」にデータを追記 > 4番がうまくいきません。 「追加データファイルオープン」のところで「>>」の指定がないので 期待通りに動かないのではないですか?

trfnc223
質問者

補足

ご回答ありがとうございます。 追記モードにするということでしょうか。 試してみましたけど変わりありませんでした。

関連するQ&A

  • ファイルの書き込み

    以下のことがしたいです。 testフォルダよりテキストファイル達を読み込み、 ある行だけ取り除いたのちnewtestフォルダに書き出したいと思います。 以下、コードになります。 #!/usr/bin/perl $n_dir = "newtest/"; $b_dir = "test/"; opendir(DIR, $b_dir); while($file = readdir(DIR)){ $nfile="$n_dir$file"; $bfile="$b_dir$file"; if (-T $bfile) { open(IN, $bfile); #testフォルダよりファイルの読み込み @list = <IN>; close(IN); open(OUT,">$nfile"); #newtestフォルダに書き出し while(@list = <IN>) { #testフォルダ内のファイルを一行ずつ読み込み if (@list =~ /test/) { #行にtestという文字列があるぎょうだけ書き出し print OUT @list; } } close(OUT); } else{next;} } closedir(DIR); ということをやりたいのですが、 書き出されるファイルは空です。 どこが間違っているのでしょうか。 エラーなどは特にありません。 testフォルダのファイル達が そのままnewtestフォルダに書き出されるのですが、 中身は空です。 ご協力お願いします。

    • ベストアンサー
    • Perl
  • ファイル読み込み・書き込み

    下記のようにtestディレクトリ内のファイルを読み込み、 それぞれにあるデータを足して、 読み込んだファイルを他のディレクトリに書き出したいのですが、 どのようにしたらよいのでしょうか。 とりあえず、下記はファイルを読み込んで、 なにも足さずそのまま別のディレクトリに移すとこまで、 やりたいと思っていますが、 どうやっていいものか・・・ #!/usr/bin/perl $dir = "test/"; opendir(DIR, $dir); while($file = readdir(DIR)){ open(IN, ">>$dir$file"); @list = <IN>; foreach $dataLine(@list) { print IN $dataLine; } close(IN); } closedir(DIR); ようはファイルに追記したいのですが、 上書きしないで、バックをとっておき、 別のところへ追記したバージョンを生成したいということです。 ご協力お願いします。

    • ベストアンサー
    • Perl
  • perlでCSVをソートする方法について

    perl初心者です。いつもありがとうございます。 perlでcsvファイル(1行のカラム数は200)、総行数は約3万行のファイルを37番目のカラム(-25以上25未満の数値データ)で降順ソートしその値によって行数がだいたい均等になるよう3分割し、2番目のカラムに文字でも数字でもよいのですがその4つのグループごとにフラグ(例えば1,2,3)を入れたいと思ってます。グループ化については境目の37番カラムの値は重複している場合が多いと思うのですがその場合は下(別に上でもかまいません)に入れるものとします。 ソートロジックは過去の質問を参照して理解しましたがグループ化しフラグを入れるルーチンがうまく作れません。下記のように作ったのですがこの先同じことを何度もやらなくてはならないので先に進めません。どなたかお助けください。最終的にやりたいことはカラム37でグループ化→カラム2にフラグを立てる、次にカラム2とカラム38(-25から0までの数値)でソートし同様に同じ行数になるようにグループ化→カラム3にフラグを立てる、さらにカラム2とカラム3とカラム39(-25以上25未満の数値データ)でソートし・・・同様に繰り返し最終的に1グループが100件(行)~150件(行)になるようにしたいのです。つまり約3万件のデータを3*4*2*4*2=192分割(5列の値で分類)したい、そしてどのような範囲で分割したかという情報も得たいのです。 use strict; use warnings; use utf8; use Encode; binmode STDOUT, ':encoding(utf-8)'; my $dir = './data'; # 処理するディレクトリ my $motoFile = 'customer.txt'; # もとファイル open my $fh, '<:encoding(cp932)', "$dir/$motoFile" or die 'ファイルが開けません。',"$!"; my %sorted; while (my $line = <$fh>) { my $key = (split /,/, $line)[37]; push @{$sorted{$key}}, $line; if (@{$sorted{$key}} == 1000) { open OUT, '>>:encoding(cp932)', "$dir/$key.tmp" or die "Can't open: $!"; print OUT @{$sorted{$key}}; close OUT; @{$sorted{$key}} = (); } } open OUT, '>:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!"; foreach my $key (sort { $b <=> $a } keys %sorted) { if (-e "$key.tmp") { open IN, '<:encoding(cp932)', "$dir/$key.tmp" or die "Can't open: $!"; print OUT while <IN>; close IN; } print OUT @{$sorted{$key}} if @{$sorted{$key}}; } close OUT; #↓↓↓↓ここからフラグを作成するルーチン # 行数を調べ3つに分けるルーチン my @colum37; open IN, '<:encoding(cp932)', "$dir/out.txt" or die 'ファイルが開けません。',"$!"; my @in = <IN>; close IN; my $gyousuu = scalar(@in); my $amari = $gyousuu % 3; if ($amari == 0) { my $groupGyousuu = ($gyousuu-$amari)/3; print "総行数は$gyousuu","で、1グループの行数は$groupGyousuu","ほど、余りは$amari\n"; # あまりが0の時、group1は@inの0行 ~$groupGyousuu-1行まで #         group2は@inの$groupGyousuu行 ~$groupGyousuu*2-1行まで #         group3は@inの$groupGyousuu*2行~$groupGyousuu*3-1行まで foreach my $num (1..2) { push @colum37, (split /,/, $in[$groupGyousuu*$num])[37]; # これは境目の先頭の37番目 } print "@colum37\n"; #これでここまでは完成、分けるべき値がこの配列に入っている。 open OUT, '>:encoding(cp932)', "$dir/out.txt" or die "Can't open: $!"; foreach my $line (@in) { my @line = split /,/,$line; if ($line[37]>=$colum37[0]) { $line[1] = 1; }elsif ($line[37]>=$colum37[1] and $line[37]<$colum37[0]) { $line[1] = 2; }elsif ($line[37]<$colum37[1]) { $line[1] = 3; } $line = join (',',@line); print OUT $line; } close OUT; } elsif ($amari == 1) { この後未作成

    • ベストアンサー
    • Perl
  • ファイルの入出力2

    指定したフォルダ内にあるcsvファイルのデータを 指定したhtmlファイルに出力するプログラムを書いているのですが、 下記のままだとcsvファイルにあるすべてのデータをとってきてしまいます。実現させたいことは「指定したフォルダ内にあるcsvファイル2行目のデータだけファイル出力する」というものです。 どなたかご協力お願いします。 #!/usr/bin/perl #--------------------------------------------------------------# # ディレクトリを開く #--------------------------------------------------------------# opendir(DIR,"test/"); #--------------------------------------------------------------# # 出力ファイル名を指定 #--------------------------------------------------------------# $dest = "test.html"; open (OUT, ">$dest") or die "$!"; #--------------------------------------------------------------# # 出力ファイルにhtmlを出力 #--------------------------------------------------------------# print OUT '<html> <head> <title>無題ドキュメント</title> <style type="text/css"> <!--.style1 {color: #FFFFFF}--> </style> </head> <body> <table width="419" height="105" border="0" cellpadding="0" cellspacing="0"> <tr> <th bgcolor="#000000"><span class="style1">名前</span></th> <th bgcolor="#000000"><span class="style1">住所</span></th> <th bgcolor="#000000"><span class="style1">性別</span></th> </tr>'; #--------------------------------------------------------------# # ディレクトリにあるファイル名を取得 #--------------------------------------------------------------# while ($filename = readdir(DIR)) { # ディレクトリにあるファイルパスを取得 $path = "test/$filename"; if(-f $path) { # ファイルオープン open (IN, $path) or die "$!"; while (<IN>) { # カンマ区切りでデータを取得&改行削除 chomp(@data = split(/,/, $_)); print OUT "<tr>\n"; # 各行の3つのカンマ区切りデータを取得 print OUT "<th bgcolor=\"#666666\">",$data[0],"</th>\n<td>",$data[1],"</td>\n<td>",$data[2],"</td>\n"; print OUT "</tr>\n"; } close(IN); } } #--------------------------------------------------------------# # ディレクトリを閉じる #--------------------------------------------------------------# closedir(DIR); print OUT "</table> </body> </html>"; #--------------------------------------------------------------# # ファイルを閉じる #--------------------------------------------------------------# close(OUT);

    • ベストアンサー
    • Perl
  • ログファイルの指定行に書込み

    open(IN,"$log") || &error("Open Error"); @data = <IN>; close(IN); while (100 <= @data) { pop(@data); } open(OUT,">$log") || &error("Write Error"); print OUT "$in{'id'}<>$in{'comment'}\n"; print OUT @data; close(OUT); ログにはID、時間、コメントが登録されています。 送信データの中に、ログに登録済みのIDがある場合には、そのIDのある行のみを書き換えたいのですが、方法がどうしてもわかりません。 $logに記録されるIDの順番は以下のようにランダムです。IDに登録される文字列は1からの数値のみです。 52<>コメント 120<>コメント 35<>コメント 8<>コメント 2<>コメント 19<>コメント 85<>コメント よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • 指定したフォルダにアップロード

    Perlを使用してアップローダーを作成しました。 今までは # 受信データを書き込む open OUT,">$fnme"; binmode OUT; print OUT $in{'upfile'}; close OUT; としていたんですが、これではファイル数が多くなった時にエライことになるので、ディレクトリを作成してその中に…と思いまして。 で、ディレクトリをオープンが opendir DIR, "$folder_name"; ということはわかったのですが、 これを上記と組み合わせるにはどうしたらいいのでしょうか? ためしに opendir DIR, "$folder_name"; open OUT, ">$fnme"; binmode OUT; print OUT $in{'upfile'}; close OUT; closedir folder; としてみましたが、普通にcgiがあるフォルダにアップロードされてしまいました(^^; ディレクトリの中のファイル名を取得とかはあったんですが、指定のフォルダにアップロード、が見つからなくて…。

    • ベストアンサー
    • Perl
  • テキストファイルへの書き込み

    gstrMyFileにファイルへのパスが入っているとして、以下を実行すると 今あるファイルへ追記されてしまいます。 今ある行を削除をして新たにbuf()のデータを記入したいのですが、できますか? fileNo = FreeFile Open gstrMyFile For Append As #fileNo For i = 0 To 50 Print #fileNo, buf(i) Next i Close #fileNo

  • ファイルの最後の行から表示させる(最新情報を5回分だけ表示)

    ファイルの最後に1行追加し、6行以上になるとファイルの先頭を1行削除。 表示は最後の行から行うスクリプトを作りたいのです。 新しい情報を5回分だけ表示する様にしたかったのですが、余りにも力技なのでもう少しスマートにやる方法を教えてもらえませんか? ----sort.txt---- 1a 2b 3c 4d ---------------- #!/usr/bin/perl $fname = "sort.txt"; print "Content-type: text/html;\n\n"; $aaa="5e"; open(OUT, ">>$fname"); flock(OUT, LOCK_EX); print OUT "$aaa\n"; flock(OUT, LOCK_NB); close(OUT); open(IN, "$fname"); $cnt = "0"; while($line = <IN>){ $a[$cnt] = "$line"; $cnt++; } close(IN); if($cnt > 5){ open(OUT, ">$fname"); flock(OUT, LOCK_EX); $cnt = "0"; while($cnt < 6){ if($cnt > 0){ print OUT "$a[$cnt]"; } $cnt++; } flock(OUT, LOCK_NB); close(OUT); } $cnt = "0"; open(OUT, "$fname"); @buff = <OUT>; close(OUT); foreach $line ( reverse @buff ){ $cnt++; print "$line<br>\n"; } exit();

    • ベストアンサー
    • Perl
  • 一つのテキストファイルと複数のファイルの結合

    よろしくお願いします.ディレクトリ内の一つのテキストファイル(joint.txt)と複数のファイルの結合を行ごとに隣へ結合するプログラムを作成しています.ここで以下のプログラムを作成したのですが,うまくいかないため,誤っている部分をご指摘願えないでしょうか. my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); open(FILE, $dir) or die "$dir: $!"; open(FILE2,"joint.txt"); my @file = <FILE>; my @file2 = <FILE2>; close(FILE); close(FILE2); foreach my $line (@file) { foreach my $line2 (@file2) { chomp $line2; $line = "$line2.",".$line"; } } open(NEWFILE, "> $dir") or die "$dir: $!"; print NEWFILE @file; print NEWFILE @file2; close(NEWFILE); } closedir(DIR);

    • ベストアンサー
    • Perl
  • CSVデータの同じファイルに上書きするには。

    CSV形式でデータdata.csvが書いてあります。プログラムを実行して、そのファイルの$data[4]の値が5という数字だった場合は、そこのセルだけ"解除"という文字に置き換えて(ほかに入ってる値ははそのまま)data.csvに上書きしたいのですが、どうもうまくいきません。したのように記述したのですが、どこが間違っているのかがわかりません。。どなたか教えてください。よろしくおねがいします。 #!/usr/bin/perl $file='data.csv'; open(FILE, "$file"); while(<FILE>){ @data = split(/,/, $_); } close(FILE); if($data[4] eq "5"){$data[4] = "解除";} open(OUT, ">$file"); print OUT @data; close(OUT);