在 Fortran 90 中,按行将数组写入文本文件的好方法是什么?
我是 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,依此类推。
但是,当我在一个更复杂的程序中使用上述想法(即上面的最后一个倒数第五行、倒数第四行、倒数第三行和倒数第二行代码)时,我运行陷入麻烦;看起来,每一行只是间歇性地分隔(由新行)。 (我没有发布,也可能不会发布,这里是我的整个复杂的程序/脚本——因为它相当长。)我的复杂程序/脚本中缺乏一致的行分隔符可能表明我的代码中存在另一个错误,而不是上面的四行写入文件例程,因为上面的简单示例似乎工作正常。不过,我想知道,您能否帮我想想是否有更好的按行写入文本文件例程我应该使用?
非常感谢您抽出时间。我真的很感激。
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). I have tried the following, and it seems to work in the following simple example:
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
As I said, this seems to work fine in this simple example: the resulting text file, aoutput.txt, contains the numbers 1-762 on line 1, numbers 763-1524 on line 2, and so on.
But, when I use the above ideas (i.e., the last fifth-to-last, fourth-to-last, third-to-last, and second-to-last lines of code above) in a more complicated program, I run into trouble; each row is delimited (by a new line) only intermittently, it seems. (I have not posted, and probably will not post, here my entire complicated program/script--because it is rather long.) The lack of consistent row delimiters in my complicated program/script probably suggests another bug in my code, not with the four-line write-to-file routine above, since the above simple example appears to work okay. Still, I am wondering, can you please help me think if there is a better row-wise write-to-text file routine that I should be using?
Thank you very much for your time. I really appreciate it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这里有几个问题。
最根本的一点是,您不应该使用文本作为大量数据的数据格式。它很大而且很慢。文本输出对于您要自己阅读的内容很有用;你不会坐下来打印 381 万个整数并翻阅它们。正如下面的代码所示,正确的文本输出比二进制输出慢大约 10 倍,大 50%。如果您转向浮点值,则使用 ascii 字符串作为数据交换格式会存在精度损失问题。 如果您的目标是与 matlab 交换数据,那么
将数据写入 matlab 可以读取的格式是相当容易的;您可以使用 matlab 中的 matOpen/matPutVariable API,或者只是将其写为 matlab 可以读取的 HDF5 数组。或者您可以直接用原始 Fortran 二进制文件写出数组,如下所示,并具有 matlab 读取。
如果您必须使用 ascii 来写出巨大的数组(如上所述,这是一个糟糕且缓慢的想法),那么您将遇到列表定向 IO 中默认记录长度的问题。最好的方法是在运行时生成一个正确描述输出的格式字符串,对于如此大的(~5000 个字符宽!)行,最安全的方法是将记录长度显式设置为大于要打印的长度因此 fortran IO 库不会帮助您打破线路。
在下面的代码中,
生成字符串 rowfmt,在本例中为
(762(1X,I6))
,这是您将用于打印输出的格式,以及RECLOPEN
的 code> 选项将记录长度设置为大于 7*numcols + 1。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,
generates the string rowfmt which in this case would be
(762(1X,I6))
which is the format you'll use for printing out, and theRECL
option toOPEN
sets the record length to be something bigger than 7*numcols + 1.这可能是一种非常迂回且耗时的方法,但无论如何......您可以简单地使用
advance='no'
单独打印每个数组元素(以禁止插入换行符在您的write
语句中打印内容之后。完成一行后,您可以使用“正常”write
语句来获取换行符,然后从下一行重新开始。这是一个小例子:当您运行该程序时,输出如下:
This may be a very roundabout and time-consuming way of doing it, but anyway... You could simply print each array element separately, using
advance='no'
(to suppress insertion of a newline character after what was being printed) in yourwrite
statement. Once you're done with a line you use a 'normal'write
statement to get the newline character, and start again on the next line. Here's a small example:When you run this program the output is as follows:
使用“*”是列表定向 IO——Fortran 将为您做出决定。有些行为没有指定。您可以使用格式语句获得更多控制。如果您想明确识别行边界,请在每行后面写一个标记符号。类似这样的内容:
几个小时后的附录:
也许对于较大的 numcols 值,对于您用来检查文件的某些程序来说,行太长了?对于输出语句,尝试:
这会将矩阵的每一行(如果矩阵超过 10 列)分解为文件中的多个较短的行。
Using an "*" is list-directed IO -- Fortran will make the decisions for you. Some behaviors aren't specified. You could gain more control using a format statement. If you wanted to positively identify row boundaries you write a marker symbol after each row. Something like:
Addendum several hours later:
Perhaps with large values of numcols the lines are too long for some programs that are you using to examine the file? For the output statement, try:
which will break each row of the matrix, if it has more than 10 columns, into multiple, shorter lines in the file.