• 締切済み

Perl 高速でファイルを結合させたい

ある集計データを作成しているのですが、 とにかく遅いです。 下記のソースで、繰り返しファイルをオープンさせ 結合を繰り返しているからでしょうが これを早くするにはどうすればいでしょうか? ※やりたいことは実際はできております。 まず条件として ディレクトリのデータは数年分あり 今回は、4年分の統計を取りたい。 →2018年度、2017年度、2016年度、2015年度 ディレクトリのファイルの数、ファイルの名前は一緒(例は数字ですが、基本名前です) →ファイルの数は15個 →ファイル容量 大きいので1500kbの約2万行 少ないので600kbほど 合計ファイル容量1年分→1mbほど 4年分になると4mb(単純計算) 平均ファイル容量750kbほど my @DATAFILE; my @FILENAME = ("T1","T2","T3","T4","T5","T6","T7","T8","T9","T10","T11","T12","T13","T14","T15"); my $Year = 2018; my $YearM = $Year - 3; for(my $i=0;$i<=$#FILENAME;$i++){ my $FILE = $FILENAME[$i]; chomp($FILE); for(my $pr=$YearM;$pr<=$Year;$pr++){ open(FILE, "<","data/log/$pr/$FILE.txt"); eval{ flock(FILE, 1) }; my @DATASUM = <FILE>; close FILE; push @DATA,@DATASUM; } } どうか改善方法を教えください。

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

みんなの回答

  • wormhole
  • ベストアンサー率28% (1622/5658)
回答No.3

取りあえず条件がおかしいです。 小さいファイルで600KB、 1年分の15ファイルが全て小さいファイルだったとしても、 600KB x 15 で 9000KB→約9MB ですよ(1ファイルの容量が間違っているのか、1年分の容量が間違っているのかはわかりませんが)。 処理速度には影響しないでしょうが >chomp($FILE); は不要("T1"~"T15"の文字列から末尾の改行を取り除く意味はないですよね。元からないんですから)。 大きいファイルが1500KBで約2万行ということは 平均ファイル容量の750KBだと約1万行ということろでしょうか。 それを60ファイルということですから >push @DATA,@DATASUM; の部分は@DATAに最終的に60万要素のpushをしてることになりませんか? 試していませんが行単位で読み込むのではなくファイル単位でまるまる読み込み連結後にsplitした方が早いんじゃないでしょうか。

  • STICKY2006
  • ベストアンサー率29% (1536/5269)
回答No.2

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11193930196 他のとこでも質問されてるなら、わざわざ自分が書く必要もなさそうなので簡単にだけ。 「どうしてもPerlでやらなければいけない」前提の話なら ・前にも張り付けたけど、わざわざファイル開かんでも、結合するコマンドでくっつけるのを試してみりゃいい ・1行でもコード減らせば処理時間がコンマ秒は減るんだろうから、ソースを見直す とかぐらいでしょうか。 自分なら ・サーバ上でバッチ処理組んどく ・30秒ぐらいじゃ待つ(質問文からは、「高頻度の作業である」とは読み取れなかったので) ぐらいでしょう。多分。 504エラーは、サーバ設定でもいじりゃ、どうにかなるでしょう。

  • STICKY2006
  • ベストアンサー率29% (1536/5269)
回答No.1

>>Perl 高速でファイルを結合させたい Perlである必要性は?? >>とにかく遅いです。 表現が主観すぎます。 「数字」として記載した方がよろしいかと。(で、これをどのぐらいにする必要がある。と。 「そんなに遅いなら、手作業でやったら?6~70ファイル程度なんでしょ?」 と個人的には思ってしまいますし。 >>繰り返しファイルをオープンさせ結合を繰り返しているから オープンしないで結合すりゃいいんじゃないすかね。 ファイルAのお尻にファイルBの内容を追記して、ファイルCを作成する。 ような無料ツールぐらいならゴロゴロと落ちてると思いますよ。 最悪、2万行程度のテキストファイルなら、手作業で「開いてコピって張り付けて」を繰り返したって、そこまで時間かかる作業ではないと思いますし。。。 15ファイルが、4年分。ですよね。死ぬほど多い数じゃない。 ファイル分割・結合 https://www.vector.co.jp/vpack/filearea/win/util/file/spl_mrg コマンドプロンプトの copy コマンドでファイルを結合する https://futuremix.org/2009/10/combile-file-with-copy-command コマンドプロンプトで2つのファイルを結合する http://cmd-pro.com/m_com.html http://q.hatena.ne.jp/1361688412

programstudy11
質問者

お礼

御回答ありがとうございます。 Perlである必要性 →ウェブ上での作業のため 遅い →30秒ほどかかったりひどいときは504エラー 手作業 →手作業ですべて結合した後にオープンでも時間がかかった・・・ です・・・

関連するQ&A

  • Perl 速度について

    あるサイトの情報を取得するのに ソースを組んで、なんとかできたのですが、 時間がかかってる気がします。 ページ数 約300ページくらい読み込んでいて その中でも、 250ページほど、正規表現で、一部抜粋し、保存させているのですが、 ここが遅いです。 保存した容量は、 1個につき2kb~20kb 平均8kbほど 下記のような、ソースの箇所が明らかに遅い気がします。 my @words; while ($content =~ m#<td.*?>(.+?)</td>#gs) { my $word = $1; $word =~ s/<.+?>//g; $word =~ s/(\r|\n)+/\,/g; $word =~ s/,+//g; for( my $year=2010; $year<2017; $year++ ){ $word =~ s/$year\//\n$year\//g; } from_to($word,"euc-jp","utf8"); utf8::decode($word); push(@words, $word) if $word; } @Word = join(',', @words) . "\n"; print @Word; open(FILE, ">","$DATA[$i].txt") or die("error :$!"); flock(FILE, 2); print FILE @Word; close FILE; 内容が多いからでしょうか? だいたい10分近くかかっております。 パソコンもcore i3と古い Windows7を使用しています。 そんなものでしょうか? もしくは、速くする方法はありますか?

    • ベストアンサー
    • 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-cgiのリネームについて

    Perl-CGIで ABCという、ディレクトリの中のファイルの名前を、ランダムな名前に変換したいのですがうまくいきません。 これを動作させるたびになぜか、どんどんファイルが減っていってしまいます。 どなたか、教えていただけないでしょうか? 宜しくお願い致します。 #!/usr/local/bin/perl print "Content-Type: text/plain\n\n"; $| = 1; my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime(time); my ($nowtime) = sprintf("%02d_%02d_%02d_%02d_%02d_",$year+1900,$mon+1,$mday,$hour,$min,$sec); #ディレクトリのファイル個数を記録する $dir = "./ABC/"; # ← ディレクトリを変数にセットする opendir DIR, $dir; @files = grep { !m/^(\.|\.\.)$/g } readdir DIR; # ← 「.」 「..」 以外のファイルを取得 close DIR; srand; for (my $i = @files; --$i; ) { my $j = int rand ($i + 1); next if $i == $j; @files[$i, $j] = @files[$j, $i]; } $num = 0; use File::Copy; foreach(@files){ $getpath = "$dir"."$_"; if( copy($getpath, "$dir".$nowtime.$num++.'.dat') eq 1){ $num++; unlink($getpath);}else{print "Copy Error"; exit;} }

    • ベストアンサー
    • CGI
  • Perl BBS掲示板 サーバ側のファイル削除

    いつもお世話になっております。 サーバ側のファイルを削除したいのですが、 どのようにすればいいのでしょうか・・・? #-----------------------------------------DELETE sub deletedata { use CGI; my $form = new CGI; my $tm = time; my $DELETE = $form->param('pass'); my $NO = $form->param('no'); my $NAME = TransFormdata($form->param('name')); my $TITLE = TransFormdata($form->param('title')); my $TEXT = TransFormdata($form->param('text')); my $img = TransFormdata($form->param('img')); open(FILE, "<$FILE") or die("error :$!"); eval{ flock(FILE, 1) }; @DATA = <FILE>; close FILE; my $DELETE = $form->param('pass'); my $NO = $form->param('no'); if(length $NO == 0) {} elsif($DELETE eq $PASSWORD) { deleteImg($NO); splice @DATA, $NO, 1; } else{} open(FILE, ">$FILE") or die("error :$!"); eval{ flock(FILE, 2) }; print FILE @DATA; close FILE; &endform } #-----------------------------------------DELETE #-----------------------------------------UPFILEDELETE sub deleteImg { my ($NO,$tm,$NAME,$TITLE,$TEXT,$imgname) = split(/,/, $DATA[$_[0]]); $filename = $DIR . $imgname; if(-e $imgname) { unlink $filename; } } #-----------------------------------------UPFILEDELETE ラジオボタン選択 ↓ パスワード入力 ↓ 削除ボタン ↓ 削除 この段階で テキストファイルの指定行は削除されているのですが、 画像が投稿され、サーバー側に残っているファイルも削除させたいのです。 Unlinkを使うとなっていたのですが・・・わからないです。 どうかご教示お願い致しますm(;;)m

    • ベストアンサー
    • Perl
  • ファイルのアップロードについて

    PERLでファイルのアップロード機能を作成しています。 アップロードと言っても、サーバへのコピーではなく、 データベースへバイナリで格納しなければならないのです。 とりあえず2パターン作成してみたのですが、 OPEN関数を使ってやるとうまくいきません。 -----パターン1(OPEN関数使用)------------- $query = new CGI; $filename = $query->param('msds'); # ファイル名(フルパス)取得 if($filename ne "") { open(ATT, $filename);# or die "Could not open atachment file:"; binmode(ATT); while($bytesread = read(ATT, $buffer, $BUFSZ)){ $file .= $buffer; # ファイルサイズ制限 $file_size ++; if($file_size > 300){ exitError("ファイルサイズが大きすぎます。600KB 以下にして下さい。"); } } close(ATT); } -----パターン2(OPEN関数未使用)------------- $query = new CGI; $filename = $query->param('temp1'); # ファイル名(フルパス)取得 if($filename ne "") { while($bytesread = read($filename, $buffer, $BUFSZ)){ $file .= $buffer; # ファイルサイズ制限 $file_size ++; if($file_size > 300){ exitError("ファイルサイズが大きすぎます。600KB 以下にして下さい。"); } } print "FILE DATA:" . $file . "<BR>"; } パターン2でprint $fileをすると、ファイルの内容が 画面表示されるのですが、パターン1だと、While文に 入ってくれませんでした。 原因がさっぱりわかりません。。。 お分かりになる方が見えましたらご教授願います。

    • ベストアンサー
    • CGI
  • not enough argument in 3rd line & does not show the right response

    test.datの中身は以下の通り。 line a b c d 1 1a 1b 1c 1d 2 2a 2b 2c 2d 文字と文字の間の空間はタブです。 open (my $SIM_FILE, "test.dat") or die "can't open the file\n"; my @cols = split /\t/, readline($SIM_FILE); my %simulation = &read(@col); close $SIM_FILE; while ((my $key, my $value) = each(%simulation)) { print "$key : $value\n"; } sub read { while (my $line = readline($SIM_FILE)) { chomp $line; next unless ($line); my @values = split /\t/, $line; if (@values == @cols) { my %tmp_hash; my $year for (my $i = 0; $i<@cols; $i++) { $tmp_hash{$cols[$i]} = $values[$i]; $year = $values[$i] if ($col[$i] =~ /^year$/i); } $simulation{$year} = \%tmp_hash; } } }

    • ベストアンサー
    • Perl
  • Cでのファイルからの入力およびファイルへの出力

    こんばんわ。質問があります。Cでのファイルの入出力についてなんですが、「fscanf」と「fprintf」を用いてあるデータファイルからデータを取り込んで処理をしてファイルに書き出すプログラムを作成したのですが、私が作成したプログラムの場合、まずfscanfでファイルをすべて読み込んでしまって、それから処理をして書き出すというプログラムなのですが、この方法ではなく、はじめに最初の1行だけを読みこんでその1行だけを処理して書き出し、次に2行目だけを読み込んで処理して書き出し、そして3行目を読み込んで処理・・・・という風に1行ごとに読み込みおよび書き込みを行っていくプログラムを作成したいのですがどのようにプログラムを組んでよいのか分かりません。アドバイスがいただけると幸いです。よろしくお願いいたします。 ちなみに私が作成したプログラムは下記のようなものです。 #include <stdio.h> #define NDATA 10000 #define NMAX 40 main() { double x[NDATA], y[NDATA], z[NDATA]; double xx[NDATA], yy[NDATA], zz[NDATA]; int i, n=0 FILE *in_file, *out_file; char in_filename[NMAX], out_filename[NMAX]; printf("Input filename: "); scanf("%s", in_filename); in_file = fopen(in_filename, "r"); while(fscanf(in_file, "%lf %lf %lf", &(x[n]), &(y[n]), &(z[n])) != EOF) n++; fclose(in_file); for(i=0; i<n; i++) xx[i] = x[i]*y[i]; yy[i] = y[i]*z[i]; zz[i] = z[i]*x[i]; printf("Output filename: "); scanf("%s", out_filename); out_file = fopen(out_filename, "w"); for(i=0; i<n; i++) fprintf(out_file, "%f %f %f\n", xx[i], yy[i], zz[i]); fclose(out_file); }

  • ファイルからデータを読み込んで配列に格納するには

    読んでいただいてありがとうございます。 Perlをはじめたばかりなのと プログラミング自体もまだまだですので 困っております。 テキストデータ 例えば apple りんご banana バナナ orange オレンジ などというファイルから読み込んで、 my $data ={ my @english, my @japanese, }; などの、配列の構造体に格納するにはどのようにしたら よいのでしょうか。 最初、本を少し読んだだけの知識で、 use strict; use warnings; my $filename = 'data.txt'; my $data = { my @english, my @japanese }; open(IN,$filename) or die "$filename: $!"; while(my $line=<IN>){ for(my $i=0;$i<N;$i++){ ($data->{@english}[$i],$data->{@japanese}[$i])=split(/ /,$line); print "$data->{@english}[$i]\n"; } } close(IN); } などと書いて、 apple banana orange と表示されるのを期待したのですが、 ダメでした。 参考までに私の誤ったプログラムものせましたが、 全然違っているかもしれませんので、まったく新たに アドバイスしていただいてもかまいません。 これを応用したものを使いたいので ぜひともどなたかお教え いただければと思います。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • perl アップロード

    http://hole.sugutsukaeru.jp/archives/10 こちらのサイトでPerlでのファイルのアップロードを行なおう思い、 作成しているのですが、 エラーになり表示されません。 コードを貼り付けると、 #!/usr/bin/perl -w #使用するモジュールをロード use File::Basename; use CGI; #変数宣言 my ($form, $dir, $filename, $parsename, @filename, $error, $ok, $type, $newfile, $i, $buffer, @ext_ok); #ファイルを保存するディレクトリを設定 #(CGIの実行ユーザで書き込み権限が必要) $dir = './files'; #受付可能な拡張子(正規表現) @ext_ok = qw ( txt zip pdf doc cgi ); #CGIオブジェクトを作成 $form = new CGI; #転送できるファイルの最大サイズを設定 #(実際は、post送信されるコンテンツ合計の最大サイズ) #この値は、CGIオブジェクトを作成する時には既に #設定されていなければならない $CGI::POST_MAX = 1024 * 1000; #max = 1MB #CGIオブジェクトを作成 $form = new CGI; #クライアントにヘッダを送信 #これは、結果メッセージ表示のため binmode STDOUT; print "Content-Type: text/plain;charset=euc-jp\r\n\r\n"; #ファイルの転送のチェック if (!defined($filename) and $error = $form->cgi_error){ #ファイルが転送されていなかったら、$filename は 未定義値となっている。 #フォーム上でファイルを選択しないままフォームがサブミットされた場合は、 #通常はこの変数 $filename は空文字列として定義されている(=未定義ではない)。 #このため、以前のバージョンでは $filename が定義されている #かどうかをエラーの判別の基準としていたが、 #2007年3月 Mac OS X 上の Netscape 7.1 で試したところ、 #ファイル選択されていない場合に未定義値になることが判明。 #このため、エラーの場合に設定される(筈の)値 $form->cgi_error も判別の #基準に追加した。 print "ファイルが転送できませんでした:$error\n"; exit; } if ($filename) { #ファイルが転送されていれば、値は真 #ファイルパス内の「\」を「/」に変換 # $parsename には、送信元クライアントマシン内での #ファイルパスが格納されている。 #注:Shift_JISで実装する場合、このあたりには工夫が必要。 $parsename =~ s#\\#/#g; #ファイル名を(ベース名, ディレクトリ名, 拡張子)に分解 @filename = fileparse($parsename, "\.[^\.]+"); #ベース名のチェック(アスキー文字列であること) $filename[0] =~ /^[\.\w~-]+$/ and $filename[2] =~ /^[\.\w-]+$/ and $ok = 1; unless ($ok) { $error = 'ファイル名は、半角英数字にして下さい。'; print "ファイル転送ができませんでした。: $error\n"; exit; } $ok = 0; #フラグのリセット #拡張子のチェック foreach (@ext_ok){ $filename[2] =~ /^\.$_$/ and $ok = 1 and last; } unless ($ok){ $error = "許可されていない拡張子($filename[2])です。"; print "ファイル転送ができませんでした。: $error\n"; exit; #サーバ側ファイル名の決定 #まず、セッションごとに一意のディレクトリ名を作成 while (-d "$dir") { $dir = $dir.'/upload_'.&gen_unique_key(15); } #ファイルを格納するディレクトリを作成 unless (mkdir($dir, oct(777))){ print "保存ファイル用ディレクトリの作成に失敗しました。: $!\n"; exit; #サーバ側のファイルパスを設定 $newfile = $dir."/".$filename[0].$filename[2]; #既に同名のファイルが存在した場合 #(複数の同名ファイルを同時にアップロードした場合など)は、 #ベース名にアンダースコアと番号を付けて別名にする $i = 0; while (-f "$newfile"){ $i++; $newfile = $dir."/".$filename[0]."_".$i.$filename[2]; } #ファイルの保存 unless (open (OUTFILE,">$newfile")){ print "サーバ側の保存ファイルの作成に失敗しました。: $!\n"; exit; } #保存用ファイルを無事 open できた場合 #改行コードの自動変換を停止 binmode (OUTFILE); binmode ($filename); # $filename から内容を読み出して #保存用ファイルに書き出す #この場合、変数 $filename はファイルハンドルとして #機能する while (read($filename,$buffer,1024)) { print OUTFILE $buffer; #ファイルを close して終了メッセージを表示 #この場合、$filename は、送信元クライアント #マシン内でのファイルパス(ブラウザが送信してきた値)を返す close (OUTFILE) and print "送信されたファイル ($filename) を右のファイル名で保存しました: $newfile\n" or print "サーバ側の保存ファイルのクローズに失敗しました。: $!\n"; } else { # ファイルが転送されていない場合 # $filename は 偽 print "ファイルはアップロードされていません。\n"; } #一意の文字列を作成するための関数 sub gen_unique_key($){ #生成する文字列の長さを引数で指定 my $length = shift; my ($i, $tempval, $key, $chars, @chars); #引数で指定された文字列長さが、 # 5以上 30以下の数値でない場合、15に設定 #(範囲は、長からず短からず...。) ($length =~ m/^\d+$/ and $length >= 5 and $length <= 30 ) or $length = 15; #使用する文字を指定(ディレクトリ名として使用できる文字を指定する) $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~-_'; @chars = split(//, $chars); #乱数のタネを作る srand(time|$$); for ($i=0; $i<$length; $i++){ # @chars 配列の最大の添字までの乱数を生成する $tempval = int(rand(scalar(@chars))); $key .= $chars[$tempval]; } return $key; } となりますが、どこが間違えているのでしょうか。 上記コードは、サイトからのコピペになります。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • Perl 処理速度について

    ブログを作成し、カテゴリー別に分けたく下記のようなソースを 書きました。 sub category1 { open(FILE, "<$FILE") or die("error :$!"); eval{ flock(FILE, 1) }; @DATA = <FILE>; close FILE; $a = 0; my @category1 = grep(/,1,/, @DATA); while (<@category1>) { $a++; } &buffer(); if($start > @DATA) {$start = @DATA;} $next = $start + 100; $back = $start - 100; if ($back < 0) { $back = 0; } for ($i=$start;$i<$next;$i++){ last if ($i > @category1); $data = $category1[$i]; &page(); } } これの処理速度が遅いのですが、なにか原因はありますでしょうか? 似たような処理で下記のソースの方が、読み込む内容としては多いのですが、 こちらは普通の速度です。 sub diary2017 { open(FILE, "<diary/diary-2017.txt") or die("error :$!"); eval{ flock(FILE, 1) }; @DATA = <FILE>; close FILE; &buffer(); if($start > @DATA) {$start = @DATA;} $next = $start + 1000; $back = $start - 1000; if ($back < 0) { $back = 0; } for ($i=$start;$i<$next;$i++){ last if ($i > @DATA); $data = $DATA[$i]; &page(); } } カテゴリーのサブルーチンとアーカイブ専用のサブルーチンで違う点といえば $a = 0; my @category1 = grep(/,1,/, @DATA); while (<@category1>) { $a++; } このgrepのとこですが、 これが遅いとすると対処方法はありますか? ※開くファイルはアーカイブは直接名前にしてて、カテゴリーは変数を使ってますが  これは同じです。

    • ベストアンサー
    • Perl

専門家に質問してみよう