簡體   English   中英

在 Fortran 90 中,將數組按行寫入文本文件的好方法是什么?

[英]In Fortran 90, what is a good way to write an array to a text file, row-wise?

我是 Fortran 的新手,我希望能夠以逐行方式(列之間的空格和每一行在自己的行上)將二維數組寫入文本文件。 我嘗試了以下方法,它似乎適用於以下簡單示例:

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,依此類推。

但是,當我在一個更復雜的程序中使用上述想法(即,上面倒數第五行、倒數第四行、倒數第三行和倒數第二行代碼)時,我運行陷入麻煩; 每行似乎只是間歇性地(由新行)分隔。 (我沒有發布,也可能不會在這里發布我的整個復雜程序/腳本——因為它相當長。)在我的復雜程序/腳本中缺乏一致的行分隔符可能表明我的代碼中存在另一個錯誤,而不是上面的四行寫入文件例程,因為上面的簡單示例似乎可以正常工作。 不過,我想知道,你能幫我想想我是否應該使用更好的逐行寫入文本文件例程?

非常感謝您的寶貴時間。 對此,我真的非常感激。

這里有幾個問題。

最基本的一點是,您不應該將文本用作大量數據的數據格式。 它很大而且很慢。 文本 output 適合您自己閱讀的內容; 您不會坐下來打印 381 萬個整數並翻閱它們。 如以下代碼所示,正確的文本 output 比二進制 output 慢約 10 倍,大 50%。 如果您使用浮點值,使用 ascii 字符串作為數據交換格式會出現精度損失問題。 等等

如果您的目標是與 matlab 交換數據,則將數據寫入 matlab 可以讀取的格式相當容易; 您可以使用 matlab 中的 matOpen/matPutVariable API,或者將其寫為 matlab 可以讀取的 HDF5 數組。 或者你可以在原始 Fortran 二進制文件中寫出數組,如下所示,並讓matlab 讀取它

如果您必須使用 ascii 來寫出巨大的 arrays(如前所述,這是一個糟糕且緩慢的想法),那么您將在列表導向的 IO 中遇到默認記錄長度問題。 最好的方法是在運行時生成一個格式字符串,它可以正確描述您的 output,最安全的是對於如此大的(約 5000 個字符寬)行。將記錄長度顯式設置為大於您將要打印的長度以便 fortran IO 庫不會幫助您打破界限。

在下面的代碼中,

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

生成字符串 rowfmt ,在這種情況下為(762(1X,I6)) ,這是您將用於打印的格式,並且OPENRECL選項將記錄長度設置為大於 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

這可能是一種非常迂回且耗時的方式,但無論如何......您可以簡單地單獨打印每個數組元素,使用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

當你運行這個程序時,output 如下:

 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

使用“*”是針對列表的 IO -- Fortran 將為您做出決定。 未指定某些行為。 您可以使用格式語句獲得更多控制權。 如果您想明確識別行邊界,請在每行后寫一個標記符號。 就像是:

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

幾個小時后的附錄:

也許對於您用來檢查文件的某些程序來說,對於較大的 numcols 值,這些行太長了? 對於 output 語句,請嘗試:

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

如果矩陣的列超過 10 列,這會將矩陣的每一行分解為文件中的多個較短的行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM