我是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等等。
但是,当我在一个更复杂的程序中使用上述想法(即,上面的倒数第五个,倒数第四个,倒数第三个,倒数第二个以上的代码行)时我遇到了麻烦;看来,每一行都是间歇性地(通过一条新线)划界。 (我没有发布,也可能不会发布,这里是我的整个复杂的程序/脚本 - 因为它很长。)在我复杂的程序/脚本中缺少一致的行分隔符可能表明我的代码中存在另一个错误,而不是上面的四行写入文件例程,因为上面的简单示例似乎工作正常。不过,我想知道,如果有一个更好的逐行写入文本文件例程,我可以帮我想一想吗?
非常感谢你的时间。我真的很感激。
答案 0 :(得分:14)
这里有一些问题。
基本的一点是,您不应该将文本用作大量数据的数据格式。它很大而且很慢。文本输出适合您自己阅读的内容;你不会坐下来打印出381万个整数并翻阅它们。如下面的代码所示,正确的文本输出比二进制输出慢大约10倍,并且大50%。如果移动到浮点值,则使用ascii字符串作为数据交换格式会出现精度损失问题。等
如果您的目标是与matlab交换数据,那么将数据写入matlab可以读取的格式相当容易;您可以使用matlab中的matOpen / matPutVariable API,或者将其写为matlab可以读取的HDF5数组。或者您可以在原始Fortran二进制文件中写出数组,如下所示,并matlab read it。
如果你必须使用ascii来写出巨大的数组(如上所述,这是一个糟糕而缓慢的想法)那么你在列表drected IO中遇到了默认记录长度的问题。最好的是在运行时生成一个格式字符串,它正确地描述了你的输出,对于这么大(~5000个字符宽!)行来说最安全的是将记录长度明确地设置为大于你打印出来的值。因此fortran IO库无法帮助您分解行。
在下面的代码中,
WRITE(rowfmt,'(A,I4,A)') '(',numcols,'(1X,I6))'
生成字符串rowfmt,在这种情况下为(762(1X,I6))
,这是您用于打印输出的格式,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
答案 1 :(得分:10)
这可能是一种非常迂回且耗时的方式,但无论如何......你可以使用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
答案 2 :(得分:3)
使用“*”是列表导向的IO - Fortran将为您做出决定。某些行为未指定。您可以使用格式语句获得更多控制权。如果要确定行边界,请在每行后面写一个标记符号。类似的东西:
DO i=1,numrows
WRITE(12,*) a(i,:)
write (12, '("X")' )
END DO
几个小时后的附录:
对于某些用于检查文件的程序,如果使用较大的numcols值,则行太长了?对于输出语句,请尝试:
WRITE(12, '( 10(2X, I11) )' ) a(i,:)
将矩阵的每一行(如果有超过10列)分成文件中的多个较短的行。