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

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

締切り済みの質問

下のディレクトリ(3つ)に含まれる同じファイル名のテキストを結合し,カレントディレクトリに出力する

いつもお世話になっております.環境はWindows XPのActiveperlです.
やりたいことは「下のディレクトリ(3つ)に含まれる同じファイル名のテキストを結合し,カレントディレクトリに出力する」ことです.具体的にはいかのようにしたいと思っています.

現在のディレクトリ/a/1.txt
a
b
c

現在のディレクトリ/b/1.txt
d
e
f

現在のディレクトリ/c/1.txt
g
h
i

現在のディレクトリ/1.txt
a
b
c
d
e
f
g
h
i

ここで私は以下のプログラムを作成しました.

use strict;
use warnings;

my $dirname1 = './a/';
my $dirname2 = './b/';
my $dirname3 = './c/';

opendir(DIR1, $dirname1) or die "$dirname1: $!";

while (my $dir1 = readdir(DIR1)) {

next unless (-f $dir1);
next unless ($dir1 =~ /\.txt$/);

opendir(DIR2, $dirname2) or die "$dirname2: $!";

while (my $dir2 = readdir(DIR2)) {

next unless (-f $dir2);
next unless ($dir2 =~ /\.txt$/);

opendir(DIR3, $dirname3) or die "$dirname3: $!";

while (my $dir3 = readdir(DIR3)) {

next unless (-f $dir3);
next unless ($dir3 =~ /\.txt$/);

if (($dir1 == $dir2) && ($dir2 == $dir3)){

open(FILE1, $dir1) or die "$dir1: $!";
my $line1 = <FILE1>;
close(FILE1);
open(FILE2, $dir2) or die "$dir2: $!";
my $line2 = <FILE2>;
close(FILE2);
open(FILE3, $dir3) or die "$dir3: $!";
my $line3 = <FILE3>;
close(FILE3);

my $joint_line = $line1.$line2.$line3;

open(NEWFILE, "> $dir1") or die "$dir1: $!";
print NEWFILE $joint_line;
close(NEWFILE);
}
}
}
}
closedir(DIR1);
closedir(DIR2);
closedir(DIR3);

ですが,以下のようなエラーが発生しています.

closedir() attempted on invalid dirhandle DIR2 at joint.pl line 51.
closedir() attempted on invalid dirhandle DIR3 at joint.pl line 52.

ディレクトリハンドルが使われているけれど閉じているか実際にはディレクトリハンドルでは無い時にこれらの警告が発行されるとこの警告がでるようですが,どのようにしたら解決できるのでしょうか.よろしくお願いします.

投稿日時 - 2008-11-22 17:28:15

QNo.4498682

すぐに回答ほしいです

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

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

回答(4)

ANo.4

そもそも、あるディレクトリにあるファイル名を取り出すのに
opendir/reeaddir/closedir を使うこともないと思います。

@files = grep { -f } glob("$dir/*.txt");

てな感じで一気にリストが取れるのではないかと。

投稿日時 - 2008-11-23 02:06:50

お礼

sakusaker7様

ご回答ありがとうございます.

@files1 = grep { -f } glob("$dir1/*.txt");
@files2 = grep { -f } glob("$dir2/*.txt");
@files3 = grep { -f } glob("$dir3/*.txt");


で@filesに格納したあと,結合はどのようにするのでしょうか.
*.txtで同じ場合,結合とするのですが,ヒントをいただけないでしょうか.

投稿日時 - 2008-11-23 15:37:00

ANo.3

ん~, なんか勘違いされてるような気が....
まず,
my $dir = './a/';
my %fcount;
opendir(DIR, $dir1);
for my $filename (readdir(DIR)) {
$fcount{$filename}++ if -f "$dir1$filename" && $filename =~ /\.txt$/;
}
closedir(DIR);
とやると, ハッシュ %fcount は ./a の中にあるテキストファイルのみ 1 という値を持ちます. なので, 同じことを他のディレクトリに対しても行えば, 各ファイル名に対して「何個のディレクトリの中にあるか」がわかります.
今の場合は「3個のディレクトリにある」ファイル名に対して処理をするわけですから, %fcount の値が 3 であるようなキーに対して処理すればいいということになります.
現状では readdir が 3重ループになってますが, これなら 3回ループするだけなので高速であることが期待できます.

投稿日時 - 2008-11-22 23:47:24

お礼

Tacosan様

ご回答ありがとうございます.
以下のように組んでみたのですがうまくいきません.

use strict;
use warnings;

my $dir1 = './a/';
my $dir2 = './b/';
my $dir3 = './c/';
my %fcount;

opendir(DIR1, $dir1);
for my $filename1 (readdir(DIR1)) {
$fcount{$filename}++ if -f "$dir1/$filename" && $filename =~ /\.txt$/;
}
closedir(DIR1);
opendir(DIR2, $dir2);
for my $filename2 (readdir(DIR2)) {
$fcount{$filename}++ if -f "$dir2/$filename" && $filename =~ /\.txt$/;
}
closedir(DIR2);

opendir(DIR3, $dir3);
for my $filename3 (readdir(DIR3)) {
$fcount{$filename}++ if -f "$dir3/$filename" && $filename =~ /\.txt$/;
}
closedir(DIR);

if ($fcount == 3){

my $line = $filename1.$filename2.$filename3;
open(NEWFILE, "> ./out/$dir") or die "$dir: $!";
print NEWFILE $line;
close(NEWFILE);
}

間違っている点をご連絡ください.よろしくお願いします.

投稿日時 - 2008-11-23 16:06:13

ANo.2

あれ? ディレクトリハンドルってブロックスコープなんだっけ....
とりあえず, もとのプログラムで closedir(DIR2) や closedir(DIR3) を適切な位置 (それぞれのディレクトリハンドルを opendir したブロックの最後) に移せば, エラーそのものは出ないと思う.... けど, それだけでは正しく動かないはず.
まずはじめに 3つのディレクトリから「共通のファイル名」を選んでおき, それらに対して改めてマージするというように書いた方が安全だと思う. 今のままでも (一部を修正すれば) 動くけど, 明らかに無駄.

投稿日時 - 2008-11-22 22:08:44

お礼

Tacosan様

ご回答ありがとうございます.
それでは,入力ファイルと出力ファイル(入力名と出力名が同じため1つ)を作っておいて,それを参照してディレクトリから読み込んでマージがいいですし,早いんですね.明日早速試してみます.

投稿日時 - 2008-11-22 23:03:32

ANo.1

opendir して中身を取り出したら、次のopendirをする前にclosedirする。

投稿日時 - 2008-11-22 18:41:34

お礼

sakusaker7様

ご回答ありがとうございます.

> opendir して中身を取り出したら、次のopendirをする前にclosedirする。
とは以下のようなごとでしょうか.

use strict;
use warnings;

my $dirname1 = './1-5/';
my $dirname2 = './6-10/';
my $dirname3 = './11-12/';

opendir(DIR1, $dirname1) or die "$dirname1: $!";

while (my $dir1 = readdir(DIR1)) {

next unless (-f $dir1);
next unless ($dir1 =~ /\.txt$/);

open(FILE1, $dir1) or die "$dir1: $!";
my $line1 = <FILE1>;
close(FILE1);
closedir(DIR1); #←ここに挿入
opendir(DIR2, $dirname2) or die "$dirname2: $!";

while (my $dir2 = readdir(DIR2)) {

next unless (-f $dir2);
next unless ($dir2 =~ /\.txt$/);

open(FILE2, $dir2) or die "$dir2: $!";
my $line2 = <FILE2>;
close(FILE2);
closedir(DIR2); #←ここに挿入
opendir(DIR3, $dirname3) or die "$dirname3: $!";

while (my $dir3 = readdir(DIR3)) {

next unless (-f $dir3);
next unless ($dir3 =~ /\.txt$/);

open(FILE3, $dir3) or die "$dir3: $!";
my $line3 = <FILE3>;
close(FILE3);
closedir(DIR3); #←ここに挿入
if (($dir1 eq $dir2) && ($dir2 eq $dir3)){

my $joint_line = $line1.$line2.$line3;

open(NEWFILE, "> $dir1") or die "$dir1: $!";
print NEWFILE $joint_line;
close(NEWFILE);
}
}
}
}

そうしますと,エラーは出ないのですが出力されません.
なぜなのでしょうか.よろしくお願いします.{}の位置が違うのでしょうか.

投稿日時 - 2008-11-22 19:33:32

あなたにオススメの質問