みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

ファイルの書き込み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);

長々と申し訳ありません。
エラーなどは特にありません。
ご協力お願いします。

投稿日時 - 2008-03-11 18:43:11

QNo.3853704

すぐに回答ほしいです

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

またまた失礼。。

$data[0] と $1 の中身が空でないかチェックしても駄目ですか?
if($data[0] ne ""){~

>if($data[0] == $1){#取得したパスとdata.txtとってきた値を比較
の==をeqに変えてみても駄目ですか?
(==は、数値でない場合や文字列として認識されてしまった数字の場合、正しく比較できないことがあります。
 経験上、eqなら間違いなく比較してくれます)

ご質問の件とはまったく無関係ですが、
>open(IN, $dfile);#追加データファイルオープン
から
>close(IN);
までの間に処理をたくさん行なうと、その隙に外部からファイルアクセスがあったときに、ファイルを破壊されることがあります。
あらかじめ書き出す変数や配列を用意しておき、openからcloseまでを最短にするようにした方が安全ですよ。
前回私が書いたスクリプトが見本です。(恥)

投稿日時 - 2008-03-12 01:01:03

お礼

またまたありがとうございます!!
空のチェックをしたら$1に値が入ってなかったです。
ただ、=をeqに変えたらちゃんとでなくなりました!
つまり$1に値が入っていないので当然falseなので、
値が出ないという正しい処理になりました。
あとは$1にデータさえ入れば・・・

>までの間に処理をたくさん行なうと、その隙に外部からファイルアクセスがあったときに、ファイルを破壊されることがあります。
わざわざご指摘ありがとうございます!
とても参考になりました!!!

投稿日時 - 2008-03-12 10:44:00

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.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.

データファイルの読み込みは一回だけやれば十分だと思うけど
もう寝るので指摘だけ :)

投稿日時 - 2008-03-12 02:33:25

お礼

できました!!!!

$line =~ /path="(.[^\"]*)"/;#path取得
$id = $1;
~~略~~
if($data[0] eq $id){#取得したパスとdata.txtとってきた値を比較

こうしたらちゃんと比較されました!

ありがとうございます!

投稿日時 - 2008-03-12 11:01:47

ANo.1

> 4.「2」にデータを追記
> 4番がうまくいきません。

「追加データファイルオープン」のところで「>>」の指定がないので
期待通りに動かないのではないですか?

投稿日時 - 2008-03-11 18:56:01

補足

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

投稿日時 - 2008-03-11 23:03:27

あなたにオススメの質問