简体   繁体   English

当输入文件中缺少一个变量时,从文件中读取已知数量的变量

[英]Reading a known number of variable from a file when one of the variables are missing in input file

I already checked similar posting.我已经检查过类似的帖子。 The solution is given by MSB here Reading data file in Fortran with known number of lines but unknown number of entries in each line MSB 在这里给出了解决方案Reading data file in Fortran with known number of lines but unknown number of entries in each line

So, the problem I am having is that from text file I am trying to read inputs.所以,我遇到的问题是我试图从文本文件中读取输入。 In one line there is supposed to be 3 variables.在一行中应该有 3 个变量。 But sometimes the input file may have 2 variables.但有时输入文件可能有 2 个变量。 In that case I need to make the last variable zero.在那种情况下,我需要将最后一个变量设为零。 I tried using READ statement with IOSTAT but if there is only two values it goes to the next line and reads the next available value.我尝试将 READ 语句与 IOSTAT 一起使用,但如果只有两个值,它会转到下一行并读取下一个可用值。 I need to make it stop in the 1st line after reading 2 values when there is no 3rd value.当没有第三个值时,我需要在读取 2 个值后让它停在第一行。

I found one way to do that is to have a comment/other than the type I am trying to read (in this case I am reading float while a comment is a char) which makes a IOSTAT>0 and I can use that as a check.我发现一种方法是有一个评论/除了我试图阅读的类型(在这种情况下我正在阅读浮点数,而评论是一个字符)这使得 IOSTAT>0 我可以将它用作查看。 But if in some cases I may not have that comment.但如果在某些情况下我可能没有那个评论。 I want to make sure it works even than.我想确保它能正常工作。

Part of the code部分代码

    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

INPUT FILE is of the form输入文件的形式

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

I need to make it work even when there is no "8.00 efg"即使没有“8.00 efg”,我也需要让它工作

for this case对于这种情况

    -1.000  abcd                                                                        
     12.460 28.000                                                                              
     5.000  5.000   hijk                                                                            
     20.000 21.000  lmno

I can not use the string method suggested by MSB.我不能使用 MSB 建议的字符串方法。 Is there any other way?还有别的办法吗?

I seem to remember trying to do something similar in the past.我似乎记得过去曾尝试做类似的事情。 If you know that the size of a line of the file won't exceed a certain number, you might be able to try something like:如果您知道文件一行的大小不会超过某个数字,您可以尝试以下操作:

...
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

I'm not completely sure how portable this solution is from one compiler to the next and I don't have time right now to read up on it in the f77 standard...我不完全确定这个解决方案从一个编译器到下一个编译器的可移植性如何,我现在没有时间在 f77 标准中阅读它......

I have a routine that counts the number of reals on a line.我有一个计算一条线上实数的例程。 You could adapt this to your purpose fairly easily I think.我认为您可以很容易地将其适应您的目的。

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

If a Fortran 90 solution is fine, you can use the following procedure to parse a line with multiple real values:如果 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

The subroutine reads the next real number from a string 'string' starting at character number 'pos' and returns the value in 'value'.该子例程从字符编号“pos”开始的字符串“string”中读取下一个实数,并返回“value”中的值。 If the end of the string has been reached, 'pos' is set to zero (and a value of 0.0 is returned), otherwise 'pos' is incremented to the character position behind the real number that was read.如果已到达字符串末尾,则将“pos”设置为零(并返回值 0.0),否则将“pos”递增到读取的实数后面的字符 position。

So, for your case you would first read the line to a character string:因此,对于您的情况,您首先会将该行读入一个字符串:

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

and then parse this string然后解析这个字符串

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

where the final 'if' is not even necessary (but this way it is more transparent imho).最后的'if'甚至不是必需的(但这样它更透明恕我直言)。

Note, that this technique will fail if there is a third entry on the line that is not a real number.请注意,如果该行中的第三个条目不是实数,则此技术将失败。

You might be able to use the wonderfully named colon edit descriptor.您也许可以使用命名精美的冒号编辑描述符。 This allows you to skip the rest of a format if there are no further items in the I/O list:如果 I/O 列表中没有其他项目,这允许您跳过格式的 rest:

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

For instance with gfortran I get:例如,使用 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

This works with gfortran, g95, the NAG compiler, Intel's compiler and the Sun/Oracle compiler.这适用于 gfortran、g95、NAG 编译器、Intel 的编译器和 Sun/Oracle 编译器。 However I should say I'm not totally convinced I understand this - if c or comment are NOT read are they guaranteed to be 0 and all spaces respectively?但是我应该说我并不完全相信我理解这一点 - 如果 c 或评论未被阅读,它们是否保证分别为 0 和所有空格? Not sure, need to ask elsewhere.不确定,需要去别处问。

I know the following simple solution:我知道以下简单的解决方案:

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

But it contains "goto" operators但它包含“goto”运算符

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM