CGIを使ってCSVデータをソートする方法について

このQ&Aのポイント
  • CGIを使用してCSVデータをソートする方法について相談があります。ソートプロセスを経て、別のCSVファイルに出力する必要がありますが、サーバの負荷を考慮する必要があるようです。さくらインターネットのレンタルサーバのスタンダードプランでこの処理を実行する際に問題が発生する可能性があるかどうか、また対策がある場合は教えて欲しいです。
  • 質問者はCGIを使ってCSVデータをソートする必要がありますが、さくらインターネットのレンタルサーバのスタンダードプランでこの処理を行うことに問題があるかどうか知りたいと思っています。また、もし問題が発生する場合はどのような対策が取れるかも教えていただきたいです。
  • CGIを使用してCSVデータをソートする必要がありますが、さくらインターネットのレンタルサーバのスタンダードプランで実行することに問題があるかどうか教えてください。行数が多いデータの場合は特に注意が必要であるとのことですが、どのような対策があるかも教えてほしいです。
回答を見る
  • ベストアンサー

CGIを作動させてサーバにかかる負担の問題

CSVデータを開き、ソートして別のCSVを書き出す というCGIを設置する必要が生じています。 そこで、 open(INFILE,"test_sort_data.csv"); @Read_In = <INFILE>; close(INFILE); @Read_In2 = sort { (split(/\,/,$a))[0] <=> (split(/\,/,$b))[0] } @Read_In; open(OUTFILE,">test_sort_data1.csv"); for($i=0 ; $i<=$#Read_In2 ; $i++){ print OUTFILE "$Read_In2[$i]" ; } close(OUTFILE); というスクリプト(実際は更に改造して、このCGIを使って元CSVにソート基準になる短い文字列を1項目書き加えるためのルーチンを加えます)のCGIを動かしたいのですが、この方法はサーバのメモリを消費する可能性がありますので、行数の多いデータには注意が必要とのことです。 開こうとしているCSVは、1行あたり6項目、カンマを合わせて100文字程度になると思われ、行数は最大20,000行くらいになると思われます。 これを、さくらインターネットのレンタルサーバのスタンダードプランで動かすのはまずいでしょうか? もし問題がある場合、何か対策は無いでしょうか? PCにフリーウェアなどをインストールしたりExcel上でデータをいじることの出来ないリモート中の上司に使ってもらうためなので、Webを介して数ステップのクリック程度で作業を完了してもらわないとならないのですが… ご教示いただけないでしょうか? どうかよろしくお願い致します。

  • R4-D4
  • お礼率100% (81/81)

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

  • ベストアンサー
  • asciiz
  • ベストアンサー率70% (6638/9405)
回答No.1

そうですね、20,000行であれば、ザックリ扱うデータの5倍のメモリ量を使うと考えればいいでしょうか。 扱うデータは100文字×20,000行=2,000,000バイト=2Mバイト、5倍で1回の処理に付き10MBぐらいメモリを使うのではないかと。 さてそうすると、スタンダードプランですらメモリ割り当ては数十GBあるので、処理1回こなすのに何の問題もありません。 ただ、Webアプリケーションというのは、複数人からアクセスされます。 同時に10人が処理を行えば、そのスクリプトが動いている間10×10=100MBを食うし、同時100アクセスがあれば一時的にメモリ1GBを使う、というようなわけです。処理に時間のかかるスクリプトであれば、同時稼働する確率も上がります。 ですから逆に言えば、多少重いアプリケーションであったとしても、同時に数名・数十名程度からしか使われないのであれば(メモリ量的には)全然問題ありません。 ただし、「同時に複数プロセスで起動される可能性がある」という点には考慮しておかなければなりません。 自分だけで使うプログラムのように、ファイルを読んで、ソートして、書いて、とやってしまうと、最後の『書いて』を実行前(あるいは実行中)に別プロセスの『読んで』を実行してしまう可能性があります。 そうすると、別プロセスでは更新前のデータを読んで、そこに挿入し、出力する→前の処理が反映されなかった、なんてことが発生する可能性があるわけです。 ロックファイルを用意して、1秒ごとにチェックし、それがある間は処理を開始しない、なんてことをすればいいかもしれません。 (チェック間隔を空けないと、ロックファイルがなくなった瞬間に待たされていた複数プロセスが同時に動き出し、新ロックファイルの作成も間に合わない、なんてことがあります) あるいは、更新データをキューに追加するデータ受付のプログラムと、キューからデータを取り出して実CSVデータを更新する、と言うプログラムに分けて、非同期で動かすのも良いかもしれません。 …まあそれを自動的にやってくれるのがデータベースというものですが。 データベースを使ったプログラムの場合、他のアプリを気にせず、自分の希望するSQL文をデータベースに投げれば良いです。 データベースエンジンの方では、いろんなアプリから投げられたSQL文を順次処理して、データベースファイルを更新します。 ファイルを更新するのがデータベースエンジンだけであるので、整合性が保たれます。 ---- なんだか話が大きくなってしまいましたが、ソート済みのデータに、もう一つデータを挿入する、というだけであれば、sort関数(クイックソート)を使わず、自分で挿入プログラムを書いた方が省メモリかつ高速かもしれません。 ・挿入位置となる所までファイルを読み、書き出す ・追加データを出力 ・残りの部分を読み、書き出す これをこのまま実装した場合、消費メモリは現在読んでいるデータ1行分しか必要ないでしょう。 あるいは高速化のためにファイル全体を一気に読み書きするとしても、メモリ使用量はファイルサイズの約2倍。といった感じになるでしょう。 同時アクセスの件は考えなきゃいけないんですけど…。

R4-D4
質問者

お礼

ありがとうございます。 メモリ使用量って、そういうふうに計算するのですね。 とても参考になりました。 CGIは特定の上司しか使えないようにパスワード制限しますので、アクセス数は全く問題無さそうです。 ご教示ありがとうございました。

関連するQ&A

  • 行数でなく内容を取得するには

    70<>9<>1日目<>3<>業務の流れについての概説<> こういった中身のtest.txtがあります。 open(READ_FILE,"<test.txt"); @file = <READ_FILE>; @data = split(/<>/,@file); close(READ_FILE); open(WRITE_FILE, ">test02.txt"); print WRITE_FILE "@data"; close(WRITE_FILE); このようにすれば、test2.txtは、次のようになると思ったのですが、 70 9 1日目pm 3 新薬開発の流れについての概説 行数を表示しているようで、「1」となります。 (ためしにtest.txtの行を増やしたらその行数を反映していました) どこが間違っているのかお教えいただけますでしょうか。

    • ベストアンサー
    • CGI
  • 指定行に書込み

    open( IN, "log.cgi" ); @f_data = <IN>; close( IN ); $i = '1'; open( OUT, ">dat.cgi" ); select OUT; print "document.open();\n"; print "document.write('"; foreach( @f_data ){ if( $i > 5 ){ last; } ( $f_data1, $f_data2, $f_data3, $f_data4 ) = split( /<>/ ); $f_data1 =~ s/ //g; print "$f_data1"; $i++; } print "');\n"; print "document.close();\n"; select STDOUT; close( OUT ); 上記スクリプトで、指定された行数、又は指定された場所でdat.cgiに 書き出したいのですが、どのように書けばよろしいのでしょうか? 例えば、 ・100行目に書き出す。 または、 ・指定された文字列の間に書き出す。 (例)<!--開始-->ここに書き出す。<!--終了--> このどちらかが出来ればいいのですが、上記スクリプトをどのように変更すれば可能になるでしょうか?お手数ですが、どうぞ宜しくお願いいたします。

    • ベストアンサー
    • Perl
  • CSV取込みで最終行を取り込まない方法

    CSVの取込みで1行目のヘッダーと最終行のフッターを取り込まず 2行目~最終行の前までの中身だけを取り込みたいのですが、 1行目をスキップする方法は、下記を参照にしてなんとかできましたが、 http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1230200277 ここに最終行をはぶくという処理をいれるにはどういたらいいでしょうか? ---------------- Private Sub コマンド0_Click() Dim FSO As Object Dim InFile As Object Dim Outfile As Object Dim CsvPath As String Dim WorkPath As String Dim i As Long Const skipRow = 1 ''1行読み飛ばしの1 ''元のCSVファイルのフルパスに変更して! CsvPath = "C\JPデータ取込CSV.csv" ''新しく作るCSVファイル。このMDBがあるフォルダにWork.csv 名で作られます。 WorkPath = CurrentProject.Path & "\Work.csv" '' FileSystemObjectのセット Set FSO = CreateObject("scripting.FileSystemObject") Set InFile = FSO.OpenTextFile(CsvPath, 1) Set Outfile = FSO.createTextFile(WorkPath) '' 読み飛ばし設定 For i = i To skipRow InFile.SkipLine Next i ''新しいCSVファイル作成 Outfile.Write InFile.ReadAll ''CSVファイルの終了と解放処理 Outfile.Close InFile.Close Set Outfile = Nothing Set InFile = Nothing ''Work.CSVファイルのインポート(今のDocmd以下をコピーし、ファイル名等を変更!) 'DoCmd.TransferText ・・・・・・ DoCmd.TransferText acImportDelim, "JPご清算書インポート定義", "T01_JPご精算書", WorkPath, True ''使用済みのWork.CSVを削除 FSO.DeleteFile WorkPath ''FileSystemObjectの解放 Set FSO = Nothing MsgBox "取り込み完了" End Sub

  • ifstream/ofstream について

    こんにちは。 よろしくお願いいたします。 ifstream で定義したファイルを open し、一端 close した後再度 open しようとすると、エラーとなります。 何が原因かわかりますでしょうか? ちなみに、インファイル.txt は何も記載していないので、下記ソースのwhileループ内には入りません。 が、whileループが有ればエラー(メッセージ:3103)が発生し、 無ければ、エラーは発生しません。 環境:XP home & ボーランドC++Builder6.0 テストしたソース: void __fastcall TForm1::Button6Click(TObject *Sender) { ifstream InFile; ofstream OutFile; char cBuf[255]; InFile.open("インファイル.txt"); if (!InFile) ShowMessage("3101"); OutFile.open("アウトファイル.txt"); if (!OutFile) ShowMessage("3002"); while (!InFile.getline(cBuf, sizeof(cBuf)).eof()) { OutFile << cBuf << endl; } // このwhileループが無ければ、エラーは発生しません。 OutFile.close(); InFile.close(); InFile.open("インファイル.txt"); if (!InFile) ShowMessage("3103"); // ここでメッセージが出力されます。 }

  • csvデータの開始行と最終行を全体の4分の1で区切り処理をしたい

    csvデータの開始行と最終行を全体の4分の1で区切り処理をしたい csvデータの開始行と最終行を全体の4分の1で区切り処理をしたいと思ってます。 csvファイルは20万件あります。4分の1なので1~50000件となります。 今回は50001~100000件までを行いたいのですが、先日教えていただいたwhile($lines = <IN>) ですと、最初から1件ずつ最終行まで処理をしてしまいます。 (ここから) open(OUT,">$csv"); open(IN,"$data") || &error(" $data を読み込みopen出来ません"); while($lines = <IN>) { ($seq1,$categ,$password,$imail,$cont) = split("\,", $lines); $cont .= " "; $data = "$seq1,$cont,1\n"; print OUT "$data"; $data = ""; } close IN; close OUT; (ここまで) 今回は20万件ですが、毎回このデータ量は月次ごとに変わります。 while周辺をいじるような気がしていますがどのようになるのかがわからなかったので質問いたしました。 お手数かけます。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • [perl5.8] SJISで出力したはずのファイルにutf8フラグが

    1)SJISで以下の2行を含むファイルを作成し、   sjis.txtという名前で保存します。 "ホツカイドウ" "北海道" 2)SJISで以下のスクリプトを作成します。 #=== one.pl === use encoding 'Shift_JIS'; use open IN => ":encoding(Shift_JIS)"; use open OUT => ":encoding(Shift_JIS)"; my $infile = 'sjis.txt'; my $outfile = 'sjis2.txt'; open(IN, "<$infile"); @lines = <IN>; close(IN); open(OU, ">$outfile"); print OU @lines; close(OU); 3)SJIJSで以下のスクリプトを作成します #=== two.pl === use encoding 'Shift_JIS'; use open IN => ":encoding(Shift_JIS)"; use open OUT => ":encoding(Shift_JIS)"; my $infile = 'sjis2.txt'; my $outfile = 'sjis3.txt'; open(IN, "<$infile"); @lines = <IN>; close(IN); open(OU, ">$outfile"); print @lines; close(OU); 4)one.pl を実行し、続いてtwo.plを実行すると 以下のエラーがコマンドプロンプトに表示されます。 #------------------------------------------- D:\zipcode\utf8mondai>two.pl Wide character in print at D:\zipcode\utf8mondai\two.pl line 14. "・趣セゑスカ・イ・・セ橸スウ" Wide character in print at D:\zipcode\utf8mondai\two.pl line 14. "蛹玲オキ驕・ これは何故なのでしょうか。 エラーメッセージは、printしようとしている 文字列にutf8フラグがついているという意味 らしいです。

    • ベストアンサー
    • Perl
  • cgiでの並べ替えについて

    フォームを使用して書き込みされたログファイルを並べ替えしているんですが、意図しない動きをするんです。 ログデータは以下のような感じです。 a=***&b=***&c=***&d=***&e=*** #-----並べ替えを行う open (FILE2,"<naisen.log"); flock(FILE2,2); @log2 = <FILE2>; flock(FILES,8); close FILES; @sort2 = sort {(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7];} @log2; @sort3 = sort{(split(/[&=]/,$a))[5] cmp (split(/[&=]/,$b))[5];}@sort2; open (FILE2,">naisen.log"); flock (FILE,2); @filew = @sort3; print FILE2 @filew; close(FILE2); a=***&b=***&c=***&d=120 a=***&b=***&c=***&d=111 というデータがあった場合、意図している動きは a=***&b=***&c=***&d=111 a=***&b=***&c=***&d=120 という順番に並べ替えられるものですが、 a=***&b=***&c=***&d=120 a=***&b=***&c=***&d=111 です。 なぜ111の方が下にきてしまっているのでしょうか? また全部ではなく一部だけに並べ替えが適用されないのはナゼでしょうか?

    • ベストアンサー
    • CGI
  • 改行を含んだ文字列を正規表現で置換するには?

    正規表現で、改行を含んだ文字列を置換しようと思っています。 例えば下の「infile.txt」にある aaa bbb という(2行にわたる)文字列を zzz という文字列に変換させたい訳です。 試しに「test.vbs」のようなコードを書いてみましたが、これでは上手く行きませんでした。 上手く変換されるようにするには、どうしたら良いでしょうか? ----------------------------------------------------- ○「test.vbs」の内容 Set fso = CreateObject("Scripting.FileSystemObject") Set inFile = fso.OpenTextFile("C:\infile.txt") Set outFile = fso.CreateTextFile("C:\outfile.txt") Set regEx = New RegExp regEx.pattern = "aaa\nbbb" repStr = "zzz" Do Until inFile.AtEndOfStream tempLine = inFile.ReadLine repLine = regEx.Replace(tempLine, repStr) outFile.WriteLine repLine Loop inFile.Close outFile.Close ----------------------------------------------------- ○「C:\infile.txt」の内容 aaa bbb ccc

  • 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
  • cgiログファイルの書き込みに余計なスペースが入る。

    ---------- #ここでログファイルに書き込みを行う。 open(FH,">>log.log"); print FH"ID=$ID&COUNT=$COUNT\n"; close(FH); #ログファイルをさらに開く。 open FILE, "<log.log"; flock(FILE,2); @log = <FILE>; flock(FILE,8); close FILE; ログファイルを並び替えてその順番で書き込み。 $gyou = @log; @sort=sort{(split(/&/,$b))[1] cmp (split(/&/,$a))[1];} @log; print "<FONT COLOR=RED>@sort</FONT><BR>\n"; open(FILE2, ">log.log"); print FILE2"@sort"; close (FILE2); ---------- というように行っていますが、一番最初のID=01&とかの前に半角スペースが入ります。 下の行に行くほどスペースが増えます。 どこが原因でしょうか?

    • ベストアンサー
    • Perl