• ベストアンサー

fortranの限界って・・・

こんにちは。 プログラミングはFORTRANくらいしかやった事のない上にあまり得意ではありません。 現在、3列に並ぶ237万行ほどのデータを扱っています。 この3列目のみ、つまり237万個の数字を、 1行あたり14個で並び替えしたいと思っています。 FORTRAN使用です。OSはWinXPです。 ところが、 読み込む時点で237万行のデータ(約1GB)が読み込めません。 テストで数行の軽いデータならうまく回ります。 237万行のデータでは回らないのは、データの行数に限度があるためなのでしょうか? それとも単純なプログラムミス? どなたかお詳しい方、ご教授お願いします。 もし、OSのせいだとしたら、LINUX等ではうまく回るのでしょうか? その場合、WinとLINUXではプログラミングどう違うかも教えて頂けないでしょうか? WinのプログラムはLINUXではそのまま動きませんでしたもので・・・ 宜しくお願いします。

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

  • ベストアンサー
  • ultraCS
  • ベストアンサー率44% (3956/8947)
回答No.6

#4です 補足で大体のことがわかりました。こんな感じでしょうか、一例を挙げてみます。 私はFORTRANは77で引退したので、77で書きます。コメントはコンパイラ通らないのでご注意。 ここではシンプルに書きますが、実際は、もう少しエラーチェックなどをした方が良いでしょう。    CHARACTER*30 STRING    REAL*4 DATA(3)    INTEGER*4 IDATA(14)    OPEN(10,FILE='HOGE.TXT',STATUS='OLD')    OPEN(20,FILE='OUT.TXT',STATUS='NEW')   1 CONTINUE    NDATA=0  データのカウントを初期化    DO 5 LOOP=1,15002  15002回のループ    READ(10,'(A30'),END=9) STRING  データを読み、ファイル終端でループ脱出    if(LOOP .GT. 1) THEN  1レコード目は読み飛ばして2レコード目以降を処理     READ(STRING,'(3F10.1) DATA  数値を取り出し     NDATA=NDATA+1  データのカウントを増やす     IDATA(NDATA)=INT(DATA(3)*10000.+0.1)  整数化     IF(NDATA .EQ. 14) THEN  14個読んだら書き出す      WRITE(20,'(14I5)') IDATA      NDATA=0  データのカウントを初期化     ENDIF    ENDIF   5 CONTINUE    IF(NDATA .GT. 0) THEN  15001/14だとあまりが出るけど書くのかな     WRITE(20,'(14I5)') (IDATA(1),I=1,NDATA)     NDATA=0    ENDIF    GO TO 1   9 CONTINUE    IF(NDATA .GT. 0) THEN  読み終わりでデータのあまり対策     WRITE(20,'(14I5)') (IDATA(1),I=1,NDATA)     NDATA=0    ENDIF    CLOSE(10)    CLOSE(20)    STOP    END こんな感じでしょうか、久々のFORTRANで自信がないけど。 大きな勘違いをしているようならご指摘ください     

hinekichi
質問者

お礼

どうもありがとうございます!私の考えていた流れとほぼ同様でした!特に困っていた読み込みのところがとても参考になりました!大変勉強になりました、みなさんのお蔭で完成致しました!ありがとうございました!

その他の回答 (5)

回答No.5

No.3の補足に対する回答 ひゃぁ、学習5日目ですか! もっとも、私も40年前の入社当時、3日でFORTRANを独習させられ、すぐ仕事をあてがわれました。だからお気持ちはわかるつもりです。しかし、FORTRAN文法は既に忘却の彼方です。 > 1行目、15003行目、30005行目、45007行目・・・ 今度はこうなるのですね。 0000001 >    0000001÷15002=0...1 0000002 データ … 0015003 >    0015003÷15002=1...1 0015004 データ … 0030005 >    0030005÷15002=2...1 0030006 データ … 0045007 >    0045007÷15002=3...1 0045008 データ … 0060009 >    0060009÷15002=4...1 0060010 データ … ">"記号が入っているのは、15002×n+1(n=0,1,2・・157)行目であると既に分かっていれば、これから読み取ろうとする行番号を15002で除したときの余りが1であれば「読み飛ばし」をすれば済みます。 > 最初から(15002×n+1)行目だけを避けて… 文字データの読み方をご存知ですか? ">"の右側に改行文字があるかどうかは不問として、必用な文字数を読み込んで(A変換を使います)知らん顔していればいいのです。 【おまけ】 全てのデータをメモリーに一気読みさせるのは不適当です。 メモリーは有限だし、そのメモリーを使ってOS等目に見えないソフトが動く仕組みになっているので、データを扱う場合は、遠慮しいしいちょびっとのメモリーを都合してもらって処理します。なおメモリーはOSが管理していて、限度以上のメモリーをよこせと要求すでば、OSはこれを拒否します。これがエラーにつながるのですね。 では、どれくらいのメモリーなら確保できるかというと、64キロバイト以下とするのが無難なところです。

hinekichi
質問者

お礼

ありがとうございます! 考え方、大変参考になりました!どうやら私は文字変換で変な勘違いをしていた為に、'>'記号を読むところでつまってました。プログラムはデータ一気読みを避けて組むことが出来ました。アドバイス本当にありがとうございました!

  • ultraCS
  • ベストアンサー率44% (3956/8947)
回答No.4

#1の方が頑張っておられるようなので、余計な口を出すのははばかられるのですが ・前二列のデータは不要と言うことでいいんですね。 ・三列目だけを七行ごとに纏めると言うことでしょうか ・>が入っている行のフォーマットは固定されていますか、また、必ず、15002行おきでしょうか ともかく、「まずすべてをよみこむ」というのが、間違いの根源です(はっきり言って素人プログラミング)。 基本的には逐次処理で解決できる問題ですよ。七行読んだら書いてバッファをフラッシュ、別にカウントして、15002行目にはそれなりの処理をすれば済むだけだと思うんですがね。 作ったソースとデータの最初十行くらいを出してくれるともっとちゃんとアドバイスできるのですが

hinekichi
質問者

補足

どうもありがとうございます。 おっしゃる通り、プログラミング歴=FORTRAN歴=5日目です。 >前二列のデータは不要と言うことでいいんですね そうです。三列目のみです。(細かく言うと前二列は別に使いますが、それは解決しています) >三列目だけを七行ごとに纏めると言うことでしょうか 三列目のデータを14個並べ、次の行へ、です("FORMAT(14)"で改行は勝手にやってくれているみたいですが)。 >">"が入っている行のフォーマットは固定されていますか、また、必ず、15002行おきでしょうか 1行目、15003行目、30005行目、45007行目の、 (15002×n+1)行目(n=0,1,2,・・・157) に">"が出てきます。 出てくるのは">"のみで、このフォーマットは固定です。 (ちなみに、">"は文字や実数など、扱いがわかりません) ">"を含む「全てを読み込む」のが間違いですか、 それを聞いて一歩前進した気がします。 おっしゃられる「それなりの処理」をご教授願えますか? 参考までに、 ソースは ********************************* > -4.9 0.0000 0.0049 -4.9 0.0040 -0.0052 -4.9 0.0080 -0.0033 ・・・・・ -4.9 60.0000 -0.0544 > -2.2 0.0000 0.0035 -2.2 0.0120 -0.0028 -2.2 0.0160 -0.0924 ・・・・・ -2.2 60.0000 0.2314 > ・・・・・ ********************************** スペースがうまく表現出来ませんが、 このFORMATは"FORMAT(F10.1,F10.4,F11.4)"です。 このように、1列目が-4.9や-2.2でくくられており、これをひとブロックとすると、これが158回続きます。 ひとブロックあたり15001個行のデータ、その頭の行に">"の行があるといった感じです。 これを1行14個のデータに並べたい訳です。 このFORMATは"FORMAT(14I5)"です。 *********************************************** 49 -52 -33 -17 -74 -97 -58 3 39 25 -21 -65 -74 -32 31 62 26 -28 -42 57 80 66 30 -5 -22 -14 5 17 ・・・・・ *********************************************** このように、と言いたい所ですがこちらも上手く表現出来てません。 FORMATを参照して下さい。 説明が分かりにくかったらすみません、 どうかアドバイス宜しくお願い致します!!!

回答No.3

No.2の補足に対する回答。 たて並びデータの様子は以下に示すようになっているとします。 間違っていたら適宜調整してください。大筋ではこのようなやり方でいいはずです。 左側の7桁の数は説明の便宜上付加した行番号で、それを15000で除したきの商と余りを右にコメントしてあります。 ここで、余りの方は">"の出現回数に一致していることに気がついてください。 0000001 >    0000001÷15000=0...1 0000002 データ … 0015002 >    0015002÷15000=1...2 0015003 データ … 0030003 >    0030003÷15000=2...3 0030004 データ … 0045004 >    0045004÷15000=3...4 0045005 データ … 0060005 >    0060005÷15000=4...5 0060006 データ … という様相を呈するのであれば、読み取り行数と">"記号が現れる回数をカウントしましょう。ILINE=1 IGREATER=1が初期値となります。 ILINEは1行読む都度1増えます。IGREATERは">"記号が現れる回数とすれば、読み取り行番号を15000で除した時に、余りがIGREATERに等しければその行を読み飛ばすようにするのです。 なお読み飛ばしたときはIGREATERを1増すようにしてください。 230万行程度ならこのやり方で大丈夫ですが、余りが0になるとバグが顔を出します。そうならないよう、幸運を祈ります。

hinekichi
質問者

補足

度々すみません、ありがとうございます。 先の補足で15000毎、と言いましたが、間違いです。 1行目、15003行目、30005行目、45007行目・・・ と、15002行目毎に">"記号が入っています。 >その行を読み飛ばす についてですが、その方法を教えて頂けませんか? ご説明頂いた割り算の理論は、">"記号の行を数式で条件化するものだと理解しましたが(意図が違うようでしたら私の理解力不足をご指摘下さい)、 ">"記号が入っているのは、 15002×n+1(n=0,1,2・・157)行目 であると既に分かっています。 この行だけ、読み込むのを避ける方法はございますでしょうか? 今の私の状況は、 まず全てを読み込む、そして上記(15002×n+1)行目だけを避けて書き込む、というフローで考えてましたが、 ">"記号をどう扱っていいかわからずREAD文で止まってしまうのです。 ">"記号の部分を読み込んで処理するのではなく、 最初から(15002×n+1)行目だけを避けて読み込めないものでしょうか? 長く付き合って頂いて本当にありがとうございます。宜しくお願いいたします!!!

回答No.2

No.1の補足に対する回答。 私も早とちりしたかもしれません。 さて、14行のデータを1行にまとめると、 2,370,000÷14=169,286 となり、ざっと17万行のファイルができあがります。 行列の入れ替えのように難しく考えないで、単純に処理させればいいでしょう。ディスクアクセスが頻繁になるので、処理終了まで1時間程度はかかるかも? 1時間かかるとしても、喫茶室にお出かけになればいいでしょう。 1)ファイルDATA-1をAppendモードで開く。 2)データファイル(DATA-Xとする)をReadOnlyモードで開く。 3)DATA-Xから14行読み取る。 4)三列目の情報のみ取り出し、1レコードとしてDATA-1に   追記する。 5)改行文字を出力する。 6)以下データが尽きるまで3)-5)を繰り返す。 プログラムの終了条件は、DATA-Xから14行を読み取れなかったとき、またはEND OF FILEとなったときです。 おおまかな予想処理時間は、14行の読み取りおよび1行の書き込みにそれぞれ15msかかるとして、 (15+15)×169,286=5,078,580ms が見込まれます。これは5,078秒となり、84.6分(1時間25分)になります。 この時間が大変だというときは、28行ずつ読み取り、2行ぶんを一度に書き込むような工夫をしなければなりません。 その前に、ディスクの1セクタは256バイトであり、この単位で読み書きしますので、書き込むフォーマットも工夫する必要が出てきます。 取り出した三列目の数字を9桁のフォームで14個横並べすると126バイトになります。改行文字(CR+LF)を加えて128バイト。1セクタに二行分記録するとして、256バイトちょうどになります。これが最も効率のよい記録フォームではないかと思います。 単純とはいえ、厄介な処理ですね。

hinekichi
質問者

補足

良くわかりました、ありがとうございます! ところで、追加質問で恐縮ですが、縦並びデータは1行目、及び15000行ごとに『 > 』記号が入っており、readできなくて困ってます。 プログラム作成で『 > 』の行だけ飛ばす、 指定した行から指定した行を読み込む(これが簡単そうに見えて出来ませんでした・・DOループで2~150001まで、15003~ と指定しても1行目から読み込もうとしてしまいます)、 或いはその他の方法がございましたら是非お教え下さいませんか?

回答No.1

> 読み込む時点で237万行のデータ(約1GB)が読み込めません。 約115万行なら500MB 約64万行なら256MB 約32万行なら128MB 約16万行なら64MB 約8万行なら32MB 約4万行なら16MB 約2万行なら8MB 約1万行なら4MB 約5000行なら2MB 約2500行なら1MB この1MBというのがお使いのFORTRANの作業領域に関する制約かどうかわからないのですが、仮に1MBだとして、三列目の値に注目します。 work1ファイルには三列目の値が0~2500のものを抜き出してSORTします。 work2ファイルには三列目の値が2501~5000のものを抜き出してSORTします。 work3ファイルには三列目の値が5001~7500のものを抜き出してSORTします。 …… という具合に分類SORTしておいて、最後にwork1,work2,work3,…を一つのファイルに連結すればいいのですが、手操作による介入が必要になると思います。 これは最も単純な処理方法なのですが、業務ではSORT/MERGEというソフトを利用するのが通例です。ここに、MERGEというのは「併合」くらいの意味と思ってください。 これは二つのデータファイルがあるとき、この二種類のデータキーを互いに昇順(或いは降順)に保ちながら、新しい昇順(或いは降順)ファイルにまとめあげることを目的とします。 このやり方はdata1およびdata2ファイルをそれぞれ昇順(或いは降順)にSORTしておき、data1およびdata2の先頭から順に読み出し、大小を勘案しながらdata3ファイルに記録していくものです。 ご質問の内容はSORTなのですが、大規模データということを考えれば、データを分割してSORTし、何回かのMERGE操作を繰り返すことになりそうです。 1)データの先頭から約1000行程度を取り出し、SORTする。   これをDATA-1とします。 2)データの1001行目から約1000行程度を取り出し、SORTする。   これをDATA-2とします。 3)DATA-1とDATA-2をMERGEして、これをDATA-3とします。 4)DATA-3のファイル名をDATA-1に変更します。 5)データの2001行目から約1000行程度を取り出し、SORTする。   これをDATA-2とします。 6)DATA-1とDATA-2をMERGEして、これをDATA-3とします。 7)DATA-3のファイル名をDATA-1に変更します。 8)データの3001行目から約1000行程度を取り出し、SORTする。   これをDATA-2とします。 9)以下同様に繰り返し、元データが尽きるまで処理します。 結局、SORTの他にMERGEが必用になってきたというところですが、SORT済みの二つのファイルをMERGEする方式の説明については割愛します。それほど難しくない処理ロジックなので、研究がてら開発してみてください。 また、MERGEライブラリがあるなら、開発の手間をかけるよりもそちらを利用した方がいいです。 なお上記は1000行単位で処理していますが、2000行単位に拡大しても大丈夫だと思います。

hinekichi
質問者

補足

ご丁寧にありがとうございます。説明不足で申し訳ありませんでした!「並び替え」と書きましたが昇順、降順ではなく、単純に縦書きを横書きに変換です。 1 43 326 54 -22  ・  ・ を 1 43 326 54 -22 ・・ といった感じにです。 昇順、降順よりも単純かと思われますが、もし宜しければ再度ご教授お願いしますm(_ _)m

関連するQ&A

専門家に質問してみよう