web-dev-qa-db-ja.com

Fortran 90では、行ごとに配列をテキストファイルに書き込む良い方法は何ですか?

私はFortranを使い始めたばかりですが、2次元配列を行単位で(列間のスペース、および各行の行ごとに)テキストファイルに書き込めるようにしたいと考えています。私は以下を試しました、そしてそれは次の簡単な例でうまくいくようです:

PROGRAM test3
  IMPLICIT NONE

  INTEGER :: i, j, k, numrows, numcols
  INTEGER, DIMENSION(:,:), ALLOCATABLE :: a

  numrows=5001
  numcols=762
  ALLOCATE(a(numrows,numcols))
  k=1
  DO i=1,SIZE(a,1)
    DO j=1,SIZE(a,2)
      a(i,j)=k
      k=k+1
    END DO
  END DO

  OPEN(UNIT=12, FILE="aoutput.txt", ACTION="write", STATUS="replace")
  DO i=1,numrows
    WRITE(12,*) (a(i,j), j=1,numcols)
  END DO
END PROGRAM test3

私が言ったように、これはこの単純な例でうまく機能するようです:結果のテキストファイルaoutput.txtは、1行目に1-762の数値を含み、2行目に763-1524の数値を含みます。オン。

しかし、上記のアイデア(つまり、上記のコードの最後から5番目から最後、4番目から最後、3番目から最後、2番目から最後)をより複雑なプログラムで使用すると、まずいことになる;各行は(新しい行で)断続的にのみ区切られているようです。 (私はここに投稿していませんし、おそらく投稿しません。複雑なプログラム/スクリプト全体です-かなり長いためです。)複雑なプログラム/スクリプトに一貫した行区切り文字がないことは、おそらく私のコードに別のバグがあることを示唆しています。上記の4行のファイルへの書き込みルーチン。これは、上記の簡単な例が問題なく動作するように見えるためです。それでも、私が使用しているべきより良い行ごとのテキストへの書き込みファイルルーチンがあるかどうかを考えていただけませんか? ?

お時間をいただき、誠にありがとうございます。ほんとうにありがとう。

12
Andrew

ここにはいくつかの問題があります。

基本的なものは、かなりの量のデータのチャンクのデータ形式としてテキストを使用しないことです。大きくて遅いです。テキスト出力は、自分で読むものに適しています。 381万の整数のプリントアウトに腰を下ろして、それらをめくるつもりはありません。以下のコードが示すように、正しいテキスト出力は、バイナリ出力よりも約10倍遅く、50%大きくなります。浮動小数点値に移動する場合、ASCII文字列をデータ交換形式として使用すると、桁落ちの問題が発生します。等.

データをmatlabと交換することを目的としている場合、matlabが読み取ることができる形式にデータを書き込むのはかなり簡単です。 matlabからmatOpen/matPutVariable APIを使用するか、MATLABが読み取ることができるHDF5配列として単に書き出すことができます。または、以下のようにそのままのFortranバイナリで配列を書き出し、 matlab read it とすることもできます。

Asciiを使用して巨大な配列を書き出さなければならない場合(前述のとおり、これは良くない遅いアイデアです)、リスト駆動型IOのデフォルトのレコード長で問題が発生しています。実行時に出力を正しく説明するフォーマット文字列を生成するのが最善であり、その上、そのような大きな(〜5000文字幅!)行に対して最も安全なのは、印刷するものよりも大きい値にレコード長を明示的に設定することです。したがって、fortran IOライブラリは、行を分割するのに役立ちません。

以下のコードでは、

  WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'

この場合、(762(1X,I6))という文字列rowfmtを生成します。これは、印刷に使用する形式です。RECLオプションをOPENに設定すると、レコード長が何かに設定されます7 * numcols + 1より大きい。

PROGRAM test3
  IMPLICIT NONE

  INTEGER :: i, j, k, numrows, numcols
  INTEGER, DIMENSION(:,:), ALLOCATABLE :: a
  CHARACTER(LEN=30) :: rowfmt
  INTEGER :: txtclock, binclock
  REAL    :: txttime, bintime

  numrows=5001
  numcols=762
  ALLOCATE(a(numrows,numcols))
  k=1
  DO i=1,SIZE(a,1)
    DO j=1,SIZE(a,2)
      a(i,j)=k
      k=k+1
    END DO
  END DO

  CALL tick(txtclock)
  WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'
  OPEN(UNIT=12, FILE="aoutput.txt", ACTION="write", STATUS="replace", &
       RECL=(7*numcols+10))
  DO i=1,numrows
    WRITE(12,FMT=rowfmt) (a(i,j), j=1,numcols)
  END DO
  CLOSE(UNIT=12)
  txttime = tock(txtclock)

  CALL tick(binclock)
  OPEN(UNIT=13, FILE="boutput.dat", ACTION="write", STATUS="replace", &
       FORM="unformatted")
  WRITE(13) a
  CLOSE(UNIT=13)
  bintime = tock(binclock)

  PRINT *, 'ASCII  time = ', txttime
  PRINT *, 'Binary time = ', bintime

CONTAINS

    SUBROUTINE tick(t)
        INTEGER, INTENT(OUT) :: t

        CALL system_clock(t)
    END SUBROUTINE tick

    ! returns time in seconds from now to time described by t
    REAL FUNCTION tock(t)
        INTEGER, INTENT(IN) :: t
        INTEGER :: now, clock_rate

        call system_clock(now,clock_rate)

        tock = real(now - t)/real(clock_rate)
    END FUNCTION tock
END PROGRAM test3
16
Jonathan Dursi

これは非常にラウンドアバウトで時間のかかる方法ですが、とにかくadvance='no'を使用して各配列要素を個別に印刷できます(印刷された後の改行文字の挿入を抑制するため)。 writeステートメント。行が終了したら、「通常」のwriteステートメントを使用して改行文字を取得し、次の行から再び開始します。ここに小さな例があります:

program testing

implicit none

integer :: i, j, k

k = 1

do i=1,4
   do j=1,10
      write(*, '(I2,X)', advance='no') k
      k = k + 1
   end do
   write(*, *) ''  ! this gives you the line break
end do

end program testing

このプログラムを実行すると、出力は次のようになります。

 1  2  3  4  5  6  7  8  9 10  
11 12 13 14 15 16 17 18 19 20  
21 22 23 24 25 26 27 28 29 30  
31 32 33 34 35 36 37 38 39 40
10
canavanin

「*」を使用すると、リストが指示されますIO-Fortranが判断を下します。一部の動作は指定されていません。フォーマットステートメントを使用すると、より詳細に制御できます。各行の後にマーカーシンボルを書き込む行の境界を明確に特定します。

  DO i=1,numrows
    WRITE(12,*) a(i,:)
    write (12, '("X")' )
  END DO

数時間後の補遺:

おそらく、numcolsの値が大きい場合、ファイルの調査に使用している一部のプログラムでは、行が長すぎますか?出力ステートメントについては、以下を試してください。

WRITE(12, '( 10(2X, I11) )' ) a(i,:)

マトリックスの各行が10列を超える場合は、ファイル内の複数の短い行に分割されます。

4
M. S. B.