• 締切済み

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

  • oswll
  • お礼率57% (39/68)
  • Perl
  • 回答数4
  • ありがとう数5

みんなの回答

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

そもそも、あるディレクトリにあるファイル名を取り出すのに opendir/reeaddir/closedir を使うこともないと思います。 @files = grep { -f } glob("$dir/*.txt"); てな感じで一気にリストが取れるのではないかと。

oswll
質問者

お礼

sakusaker7様 ご回答ありがとうございます. @files1 = grep { -f } glob("$dir1/*.txt"); @files2 = grep { -f } glob("$dir2/*.txt"); @files3 = grep { -f } glob("$dir3/*.txt"); で@filesに格納したあと,結合はどのようにするのでしょうか. *.txtで同じ場合,結合とするのですが,ヒントをいただけないでしょうか.

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.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回ループするだけなので高速であることが期待できます.

oswll
質問者

お礼

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); } 間違っている点をご連絡ください.よろしくお願いします.

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

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

oswll
質問者

お礼

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

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

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

oswll
質問者

お礼

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); } } } } そうしますと,エラーは出ないのですが出力されません. なぜなのでしょうか.よろしくお願いします.{}の位置が違うのでしょうか.

関連するQ&A

  • 一つのテキストファイルと複数のファイルの結合

    よろしくお願いします.ディレクトリ内の一つのテキストファイル(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
  • ディレクトリ内のテキストファイルに対する同一処理

    よろしくお願いします。現在Linuxの環境でテキスト処理をしております。 ディレクトリ内にファイル名の異なった以下のような大量ファイルがあります。 a.txt 0,1,2,3,4,5,6,7 1,2,3,4,5,6,7,8 b.txt 2,3,4,5,6,7,8,9 3,4,5,6,7,8,9,10 これらのファイルをカンマでsplitし、左から2番目の数にだけ1を引き,下のディレクトリであるoutに出力させます。出力は以下のようになります。 ./out/a.txt 0,0,2,3,4,5,6,7 1,2,3,4,5,6,7,8 ./out/b.txt 2,2,4,5,6,7,8,9 3,4,5,6,7,8,9,10 そこで以下のようなPerlのプログラムを作成しました。 use strict; use warnings; my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); print $dir, "\n"; open(FILE, $dir) or die "$dir: $!"; my @file = <FILE>; foreach $line (@file) { my ($a,$b,$c,$d,$e,$f,$g,$h) = split(/,/, $line);      my $b = $b - 1; close(FILE); } open(NEWFILE, "> ./out/$dir") or die "$dir: $!"; print NEWFILE @file; close(NEWFILE); } closedir(DIR); ですが、出力は完了するのですが、元のファイルから計算がされていません。どこがどう間違えているのかご指摘よろしくお願い申し上げます。

    • ベストアンサー
    • Perl
  • プログラムの高速化

    いつもお世話になっております.以下のプログラムをできるだけ高速化したいと思います. use warnings; use strict; 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: $!"; while (my $line = <FILE>) { my ($a,$b,$c,$d,$e,$f) = split( /,/ , $line ); my $name = $a.",".$b; open(NEWFILE, ">> ./out/$name.txt") or die "$dir: $!"; print NEWFILE $line; close(NEWFILE); } } close(FILE); closedir(DIR); やっていることは,ディレクトリ内のテキストファイルを読み込んでいって,splitでカンマ区切りにした,$a,$bをファイル名として下のディレクトリのoutに保存していくというものです. ファイル数が数千あり,各ファイルも数千行となるため,このソースを高速化する方法はありますでしょうか.ご回答よろしくお願いします.

    • ベストアンサー
    • Perl
  • 複数行に渡る文字列の置換

    こんにちは、Perl初心者です(プログラミング全般の初心者です)。 カレントフォルダ内のテキストファイルに対して、文字列置換をするスクリプトを書こうとしています。具体的には、以下のようにストリングIDの直下に改行のみの場合(ストリングがない)は、[BLANK]という文字列を挿入したいと思っています。 TEXT_STRING_ID_001<改行> <改行> <改行> TEXT_STRING_ID_002<改行> 入門書やこのサイトの皆さまのお力を借りて、なんとか以下のようなリストを書きエラーなく置換処理ができるところまでは確認できました。 しかし、この方法だと結局1行ずつ処理していることになるので、「s/\n{3}/\n[en]\n/gm」のような置換ができません(mオプションをつけてもダメなようです)。 この問題を解決する良い方法はないものでしょうか。 (もしかすると、処理の仕方を根本から変えないといけないのでしょうか) 以下、現状のリスト: use strict; use warnings; my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); print $dir, "\n"; open(FILE, $dir) or die "$dir: $!"; my @file = <FILE>; close(FILE); foreach my $line (@file) { $line =~ s/\n{3}/\n[BLANK]\n/gm; } open(NEWFILE, "> $dir") or die "$dir: $!"; print NEWFILE @file; close(NEWFILE); } closedir(DIR);

    • ベストアンサー
    • Perl
  • テキストを参照としたPerlによる名前の変更

    よろしくお願いします。ディレクトリ内のファイル名をテキストデータを参照として変更したいと思っております。まず、以下の参照テキストがあります。 sansyo.txt 1,2,1 2,3,1 3,4,2 4,5,3 6,7,9 ・ ・ ・ ・ このファイルを利用してディレクトリ内のファイルを以下のようにリネームします。 1,2.txt → 1,2,1.txt 2,3.txt → 2,3,1.txt 3,4.txt → 3,4,2.txt 4,5.txt → 4,5,3.txt 6,7.txt → 6,7,9.txt ・ ・ ・ ・ ・ ここで私は以下のプログラムを作成しました。 sansyo.pl ------------------------------ use strict; use warnings; my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); print $dir, "\n"; open(FILE, $dir) or die "$dir: $!"; open(FILE2,"sansyo.txt"); my @file = <FILE>; my @file2 = <FILE2>; close(FILE); close(FILE2); foreach my $line2 (@file2) { my ($a,$b,$c) = split(/,/, $line2); if ($dir == $a.",".$b.".txt"){ rename ($dir, $a.",".$b.",".$c.".txt"); } } } closedir(DIR); 内容は、FILE2にsansyo.txtをforeachで1行ずつ読み込んでいき、 $a,$b.txtというファイルが$dirに読み込んだファイルにあったら、 $a,$b,$c.txtというファイルにリネームするという内容です。 ですが、いろいろ試行錯誤したもののうまくいきません。 具体的には、 Argument "1,2.txt" isn't numeric in numeric eq (==) at sansyo.pl line 23. とエラーがでて if ($dir == $a.",".$b.".txt"){ この部分でエラーが発生しているようです。 どなたか解決方法をよろしくお願いします。

    • ベストアンサー
    • Perl
  • perl サブルーチンでのファイル出力結果おかしい

    以下のコードを実行するとカレントディレクトリの配下にある すべてのファイルのリストがコンソールとファイルに出力される はずですが、コンソールに表示されているファイルの一部しか ファイルに出力されていません。 どうも、最後に do_file()を呼び出したときのファイルしか リストされていないようなのですがなぜでしょうか。 どのようにすればよいのでしょうか。 よろしくお願いします。 (Windows7, ActivePerl(v5.16.3)) ----test.pl--------------------------------------------- &do_dir('.'); sub do_dir{  open(FILE2,'>list.txt') or die "$!";  my $dirname=shift;  my $delim='/';  opendir(DIR,$dirname) or die "$!";  foreach $entry (readdir(DIR)){   next if($entry eq '.');   next if($entry eq '..');   if ($dirname=~/[\\\/]$/) {    my $delim='';   }   my $filename="$dirname$delim$entry";   if(-d $filename){    &do_dir($filename);   } else {    &do_file($filename);   }  }  close(DIR);  close(FILE2); } sub do_file{  my $filename=shift;  return unless ($filename=~/\.*$/);  print "$filename\n";  print FILE2 "$filename\n"; }

    • ベストアンサー
    • Perl
  • ディレクトリ名を取得したい

    perlでディレクトリ名だけを取得したいのですが、 なかなかうまい方法が見つかりません。 ファイル名を取得する・・・というのは結構あるのですが、 ディレクトリ名だけ、というのがどうもわかりません。 ちなみにファイル名取得は以下のようにやっています。 my $md; opendir(DIR, 'q'); while (defined($dir = readdir(DIR))) { $md=substr($dir,0,6); } closedir(DIR); ディレクトリ名だけを取得・・・というのはどうやればよいのでしょうか。

    • ベストアンサー
    • Perl
  • readdir()で得られるファイル・ディレクトリ情報の順番は?

    Perlプログラムにて、  opendir(DIR, '.');  @dir_list = readdir(DIR);  closedir(DIR); などとすると、そのディレクトリにあるファイルとディレクトリの一覧情報を得ることができますが、この場合、@dir_list にはどのような順番で情報が格納されるのでしょうか? readdir() 関数の仕様が知りたいです。ご存知の方、ぜひ教えてください。

    • ベストアンサー
    • Perl
  • テキストファイルの結合

    はじめまして。配列とか変数でFORでループさせれ ばいいのかなと思うのですが変数の取り扱いがわ かりません。あるフォルダに拡張子なしの . の 後連番のテキストファイルがあります(SGML出力)。 その連番順にテキストを結合したファイルを作成 したい。 条件として"a086.xxx"のファイルについて処理し たいです。よろしくお願いします。 ファイル名 中身 a084.000 ............. a085.000 ............. a086.000 abc.......xxx a086.001 123.......xxx ・ ・ a086.036 xyz.......xxx a087.000 ............. 結合したファイルの中身 abc.......xxx 123.......xxx ・ ・ ・ xyz.......xxx フォルダの中のそのファイルの個数はその都度 違います。 下記のように処理したいファイル名は抽出して みたのですがその後どうすればいいのかわかり ません。また、ファイル名を抽出しないでその まま処理するものでしょうか。 opendir ( DIR , "a:\\S603000" ) or die; while($dir = readdir (DIR) ){ if ( $dir =~ /a086/ ){ print "$dir\n"; } } closedir ( DIR );

    • ベストアンサー
    • Perl
  • PHPでディレクトリ名のみ表示する方法が知りたいです。以下のプログラム

    PHPでディレクトリ名のみ表示する方法が知りたいです。以下のプログラムで「test」というディレクトリ内のファイル一覧(ディレクトリ名とファイル名)を表示するようにしているのですが、この中からディレクトリ名のみ表示させたいのですがどのようにすればいいのでしょうか? よろしくお願いします。 <?php if ($dir = opendir("test/")) { while (($file = readdir($dir)) !== false) { if ($file != "." && $file != "..") { echo "$file<br>\n"; } } closedir($dir); } ?>

    • ベストアンサー
    • PHP