2

我已经检查过类似的帖子。解决方案由 MSB 在此处Reading data file in Fortran with known number of lines but unknown number of entries in each line 给出

所以,我遇到的问题是我试图从文本文件中读取输入。在一行中应该有 3 个变量。但有时输入文件可能有 2 个变量。在这种情况下,我需要将最后一个变量设为零。我尝试将 READ 语句与 IOSTAT 一起使用,但如果只有两个值,它会转到下一行并读取下一个可用值。当没有第三个值时,我需要在读取 2 个值后让它停在第一行。

我找到了一种方法来做一个评论/除了我试图阅读的类型之外(在这种情况下,我正在阅读浮点数,而评论是一个字符)这使得 IOSTAT>0 并且我可以将其用作查看。但如果在某些情况下我可能没有该评论。我想确保它甚至可以正常工作。

部分代码

    read(15,*) x
    read(15,*,IOSTAT=ioerr) y,z,w
    if (ioerr.gt.0) then
        write(*,*)'No value was found'
        w=0.0;
        goto 409
        elseif (ioerr.eq.0) then
        write(*,*)'Value found', w
        endif
  409   read(15,*) a,b
        read(15,*) c,d

输入文件的形式是

    -1.000  abcd                                                                        
    12.460  28.000  8.00 efg                                                                            
    5.000   5.000   hijk                                                                            
    20.000  21.000  lmno                                                                            

即使没有“8.00 efg”,我也需要让它工作

对于这种情况

    -1.000  abcd                                                                        
     12.460 28.000                                                                              
     5.000  5.000   hijk                                                                            
     20.000 21.000  lmno

我不能使用 MSB 建议的字符串方法。还有其他方法吗?

4

5 回答 5

0

您也许可以使用奇妙命名的冒号编辑描述符。如果 I/O 列表中没有其他项目,这允许您跳过格式的其余部分:

Program test

  Implicit None

  Real :: a, b, c
  Character( Len = 10 ) :: comment


  Do

     c = 0.0
     comment = 'No comment'
     Read( *, '( 2( f7.3, 1x ), :, f7.3, a )' ) a, b, c, comment

     Write( *, * ) 'I read ', a, b, c, comment

  End Do

End Program test

例如,使用 gfortran 我得到:

Wot now? gfortran -W -Wall -pedantic -std=f95 col.f90
Wot now? ./a.out
  12.460  28.000  8.00 efg
 I read    12.460000       28.000000       8.0000000     efg       
  12.460  28.000  
 I read    12.460000       28.000000      0.00000000E+00           
^C

这适用于 gfortran、g95、NAG 编译器、英特尔的编译器和 Sun/Oracle 编译器。但是我应该说我并不完全相信我理解这一点 - 如果未读取 c 或评论,它们是否保证分别为 0 和所有空格?不确定,需要在别处询问。

于 2012-04-11T13:58:09.957 回答
0

我有一个例程计算一条线上的实数。我认为你可以很容易地适应你的目的。

subroutine line_num_columns(iu,N,count)
    implicit none

    integer(4),intent(in)::iu,N

    character(len=N)::line
    real(8),allocatable::r(:)
    integer(4)::r_size,count,i,j

    count=0 !Set to zero in case of premature return

    r_size=N/5 !Initially try out this max number of reals
    allocate(r(r_size))

    read(iu,'(a)') line

50  continue
    do i=1,r_size
        read(line,*,end=99) (r(j),j=1,i) !Try reading i reals
        count=i
        !write(*,*) count
    enddo
    r_size=r_size*2 !Need more reals
    deallocate(r)
    allocate(r(r_size))
    goto 50

    return

99  continue
    write(*,*) 'I conclude that there are ',count,' reals on the first line'


end subroutine line_num_columns
于 2012-04-11T04:53:39.973 回答
0

我似乎记得过去尝试做类似的事情。如果您知道文件的一行大小不会超过某个数字,您可以尝试以下操作:

...
character*(128) A  

read(15,'(A128)') A  !This now holds 1 line of text, padded on the right with spaces
read(A,*,IOSTAT=ioerror) x,y,z
if(IOSTAT.gt.0)then
   !handle error here
endif

我不完全确定这个解决方案从一个编译器到下一个编译器的可移植性,我现在没有时间在 f77 标准中阅读它......

于 2012-04-11T01:16:42.170 回答
0

如果 Fortran 90 解决方案很好,您可以使用以下过程来解析具有多个实数值的行:

subroutine readnext_r1(string, pos, value)
  implicit none
  character(len=*), intent(in)    :: string
  integer,          intent(inout) :: pos
  real,             intent(out)   :: value

  integer                         :: i1, i2

  i2 = len_trim(string)

  ! initial values:
  if (pos > i2) then
     pos   = 0
     value = 0.0
     return
  end if

  ! skip blanks:
  i1 = pos
  do
     if (string(i1:i1) /= ' ') exit
     i1 = i1 + 1
  end do

  ! read real value and set pos:
  read(string(i1:i2), *) value
  pos = scan(string(i1:i2), ' ')
  if (pos == 0) then
     pos = i2 + 1
  else
     pos = pos + i1 - 1
  end if

end subroutine readnext_r1

该子例程从字符号“pos”开始的字符串“string”中读取下一个实数,并返回“value”中的值。如果已到达字符串的末尾,则 'pos' 设置为零(并返回值 0.0),否则 'pos' 递增到所读取的实数后面的字符位置。

因此,对于您的情况,您将首先将该行读取为字符串:

character(len=1024) :: line
...
read(15,'(A)') line
...

然后解析这个字符串

real    :: y, z, w
integer :: pos
...
pos = 1
call readnext_r1(line, pos, y)
call readnext_r1(line, pos, z)
call readnext_r1(line, pos, w)
if (pos == 0) w = 0.0

最后的“如果”甚至没有必要(但这样更透明,恕我直言)。

请注意,如果行上有第三个条目不是实数,则此技术将失败。

于 2012-04-11T08:30:42.307 回答
0

我知道以下简单的解决方案:

      w = 0.0
      read(15,*,err=600)y, z, w
      goto 610
600   read(15,*)y, z
610   do other stuff

但它包含“goto”运算符

于 2017-05-18T19:33:57.267 回答