2012-04-10 24 views
1

我已经检查过类似的帖子。解决方案是由M. S. B.在这里Reading data file in Fortran with known number of lines but unknown number of entries in each line当输入文件中有一个变量丢失时,从文件中读取已知数量的变量

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

I ca n不使用MSB建议的字符串方法。有没有其他方法?

+0

另一个想法,但没有提到,就是预处理与写的是一种语言的脚本输入文件更适合这类任务(python,perl甚至awk想起来)......看起来这种方法会更简单和更强大。 – mgilson 2012-04-11 11:43:10

+0

这是一个完美的建议。但是,我正在编写20年前编写的代码,此时无法更改输入文件格式。 – jonayat 2012-04-11 15:14:47

+0

我并不是建议你改变输入文件的(预期)格式,只使用脚本将原始格式不正确的文件打包成符合你的代码(20年前写的)所需的文件。 – mgilson 2012-04-11 16:21:54

回答

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

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 
+0

谢谢,我会试试这个。 – jonayat 2012-04-11 06:38:24

+0

这个计数非常漂亮,我忘记了最后的说明符。 (现在我想到了,我应该也可以在我的回答中使用它)。你应该提到的一件事是,在你计算了线上的实数之后,你需要“退格”这个单位以便能够真正获得实数。 – mgilson 2012-04-11 16:19:06

+0

@mgilson:那是真的。但不是'退格',我只是将这个例程调整到他的特定目的,并返回数组'r'和'count'。我从不相信Fortran中的文件运动。 – bdforbes 2012-04-12 03:26:20

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'开始,并返回'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 

,其中最终“如果”甚至没有必要(但这种方式是更透明的imho)。

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

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编译器,Intel编译器和Sun/Oracle编译器。不过,我应该说我并不完全相信我明白这一点 - 如果c或comment不被读取,它们分别保证为0和所有空格?不确定,需要询问其他地方。

0

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

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

但它包含“转到”运营商

+0

您可以轻松避免转到。只需使用iostat和do循环。 – 2017-05-18 20:22:31