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

前端 未结 3 1197
故里飘歌
故里飘歌 2020-12-14 04:45

I am new to Fortran, and I would like to be able to write a two-dimensional array to a text file, in a row-wise manner (spaces between columns, and each row on its own line)

3条回答
  •  离开以前
    2020-12-14 05:09

    There's a few issues here.

    The fundamental one is that you shouldn't use text as a data format for sizable chunks of data. It's big and it's slow. Text output is good for something you're going to read yourself; you aren't going to sit down with a printout of 3.81 million integers and flip through them. As the code below demonstrates, the correct text output is about 10x slower, and 50% bigger, than the binary output. If you move to floating point values, there are precision loss issues with using ascii strings as a data interchange format. etc.

    If your aim is to interchange data with matlab, it's fairly easy to write the data into a format matlab can read; you can use the matOpen/matPutVariable API from matlab, or just write it out as an HDF5 array that matlab can read. Or you can just write out the array in raw Fortran binary as below and have matlab read it.

    If you must use ascii to write out huge arrays (which, as mentioned, is a bad and slow idea) then you're running into problems with default record lengths in list-drected IO. Best is to generate at runtime a format string which correctly describes your output, and safest on top of this for such large (~5000 character wide!) lines is to set the record length explicitly to something larger than what you'll be printing out so that the fortran IO library doesn't helpfully break up the lines for you.

    In the code below,

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

    generates the string rowfmt which in this case would be (762(1X,I6)) which is the format you'll use for printing out, and the RECL option to OPEN sets the record length to be something bigger than 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
    

提交回复
热议问题