自由格式读取,不需要预先设置。

7
在给定的文件记录中,我需要首先读取前两个整数元素,然后读取行的其余部分(大量实数元素),因为任务依赖于前两个元素。假设前两个整数元素的格式没有很好地定义。
解决问题的最佳方法可能是:
read(unitfile, "(I0,I0)", advance='no') ii, jj
read(unitfile,*) aa(ii,jj,:)

但是我觉得gfortran不允许“(I0)”规范。基本上,unitfile中读取的文件可能是这样的:
0   0    <floats>
0   10   <floats>
10   0    <floats>
100   0    <floats>
100   100    <floats>

使用类Fortran的定长字段格式规范很难将其读取。

有没有其他方法可以解决这个明显微不足道的问题?


1
在gfortran中,i0不允许读取文件,但对于写入文件肯定是可以接受的。你尝试过使用i3之类的东西吗? - Kyle Kanos
好的,谢谢@Kyle。不,它在i3上不起作用:看看最后一个案例:你将无法正确读取第二个元素。 - gluuke
@KyleKanos:非常感谢,但问题在于输入文件不是由gfortran创建的。 - gluuke
1
那么你的选择要么是使用@AlexanderVogt的将行读取为字符串并根据需要重复,要么使用不同的编译器。我改变了代码并使用gcc编写了文件(fprintf(fp,"%d %d %f %f %f\n",i,j,(double)(i+j),(double)(i-j),(double)(j-i));),然后使用ifort读取它,零问题,使用read(10,'(i0,i0)',advance='no') - Kyle Kanos
2
@KyleKanos,I0在输入格式规范中不符合F2008标准。 - IanH
显示剩余2条评论
2个回答

3

这个应用了字符串操作来获取每个组件,它们被空格' '和/或制表符(char(9))分隔:

program test
  implicit none
  character(len=256) :: string, substring
  integer            :: ii, jj, unitfile, stat, posBT(2), pos
  real, allocatable  :: a(:)

  open(file='in.txt', newunit=unitfile, status='old' )
  read(unitfile,'(a)') string

  ! Crop whitespaces
  string = adjustl(trim(string))

  ! Get first part:
  posBT(1) = index(string,' ')      ! Blank
  posBT(2) = index(string,char(9))  ! Tab
  pos = minval( posBT, posBT > 0 )

  substring = string(1:pos)
  string = adjustl(string(pos+1:))
  read(substring,*) ii

  ! Get second part:
  posBT(1) = index(string,' ')      ! Blank
  posBT(2) = index(string,char(9))  ! Tab
  pos = minval( posBT, posBT > 0 )

  substring = string(1:pos)
  string = adjustl(string(pos+1:))
  read(substring,*) jj

  ! Do stuff
  allocate( a(ii+jj), stat=stat )
  if (stat/=0) stop 'Cannot allocate memory'

  read(string,*) a

  print *,a

  ! Clean-up
  close(unitfile)
  deallocate(a)
end program

For a file in.txt like:

1 2 3.0 4.0 5.0

这会导致
./a.out 
   3.00000000       4.00000000       5.00000000

注意:这只是一个快速而粗略的示例,请根据您的需求进行调整。


3

[此答案已经进行了重大修订:原始版本存在安全问题。感谢IanH指出。]

通常,如果我能避免使用非列表导向的格式化输入,我就会尽量避免。已经有一个解析字符串以实现更高通用性的答案了,但是对于一个简单的环境,我会提供一些建议。

当您对输入信任较高时,例如当仅涉及格式稍微有些棘手(或您愿意将其留给编译器进行边界检查)时,您可以采用以下方法来处理您的示例情况:

read(unitfile, *) ii, jj, aa(ii, jj, :)

或者,如果数组部分比第一列和第二列直接给出的更加复杂,可以通过表达式甚至函数来确定。

read(unitfile, *) ii, jj, aa(fi(ii,jj), fj(ii,jj), :fn(ii,jj))

使用 pure integer function fi(ii,jj) 等纯整数函数等技术相关内容。这些函数甚至可以进行范围验证(例如返回大小为0的区间)。

在更一般的情况下,但仍保持列表指向,可以使用缓冲区来处理实数变量。

read(unitfile, *) ii, jj, buffer(:) ! Or ... buffer(:fn(ii,jj))
! Validate ii and jj before attempting to access aa with them
aa(.., .., :) = buffer

缓冲区大小应适当。

你首先考虑的方法表明你对行的结构有一些合理的想法,包括长度,但当从 iijj 中未知实数的数量或者当类型(且不允许多态读取)未知时,事情确实变得棘手起来。此外,如果一个人非常在意验证输入,甚至提供有意义的详细用户反馈错误信息,那么这不是最佳选择。

最后,iostat 有所帮助。


列表定向输入可能适用于内部目的,其中您可以对输入质量进行良好的控制,但对于一般用户提供的输入而言,在验证方面严重不足,并具有“令人惊讶”的特性。 - IanH
谢谢。我已经使用gfortran测试了最简单的read(unitfile, *) ii, jj, aa(ii, jj, :),但我不确定它是否正常工作。 - gluuke
@gluuke 你想要读取的实数数量有多大?此外,aa的大小是多少?还有,它是否可以在您可以粘贴的较小情况下正常工作/失败? - francescalus
@francescalus,我终于按照你的建议使用类似缓冲区的数组解决了问题,并采用自由格式,在下一步中进行赋值。问题还在于我不知道要读取多少条记录,因此我也必须使用 iostat - gluuke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接