读取fortran中未知边界的行中的整数序列

时间:2015-08-15 06:02:16

标签: fortran line

我想在FORTRAN中读取具有未知界限的行中的整数序列。我的问题类似于以下的帖子,

Reading a file of lists of integers in Fortran

但是我想读取一行中未知数量的整数序列并将其保存在单独的数组中。连续的整数行应该保存到其他一些数组

My file looks like this
5 7 8 9 10 13            # should be stored  f(1)(6) arrays
93 102 92                # c(1)(3)
105 107 110 145 147 112  # f(2)(6)
97 98                    # b(1)(2)
12 54 55                 # c(2)(3)
15 17 21 23 45           # e(1)(5)
43 47 48 51 62           # d(1)(4)

因此,我有一个整数序列,最大长度为6(存储在f数组中),最小长度为2(存储在b数组中)。我有这样的数百行,所以我需要根据它们的最大长度进行分类。

Reading a file of lists of integers in Fortran

2 个答案:

答案 0 :(得分:2)

可能有很多方法可以做到这一点,以下是一个这样的例子。这里,split()对行中所有值的列表导向输入进行多次试验,直到遇到非数字字符或行尾。

subroutine split( line, vals, n )
    implicit none
    character(*), intent(in) :: line
    real*8  :: vals(*), buf( 10000 )
    integer :: n

    n = 1
    do
        read( line, *, end=100, err=100 ) buf( 1 : n )   !! (See Appendix for why buf is used here)
        val( 1:n ) = buf( 1:n )
        n = n + 1
    enddo
100 continue
    n = n - 1
end

program main
    implicit none
    character(200) :: line
    real*8  :: vals( 10000 )
    integer :: n

    open( 10, file="test.dat", status="old" )
    do
        read( 10, "(a)", end=500 ) line
        call split( line, vals, n )

        if ( n == 0 ) then
            print *, "comment line"
        else
            print *, nint( vals( 1 : n ) )
        endif
    enddo
500 continue
    close( 10 )
end

如果test.dat包含问题中的整行,加上以下行

# additional data
1,2,3 , 4 , 5            # comma-separated integers
1.23e2  -4.56e2 , 777    # integer/floating-point mixed case

它给出了

comment line
5 7 8 9 10 13
93 102 92
105 107 110 145 147 112
97 98
12 54 55
15 17 21 23 45
43 47 48 51 62
comment line
1 2 3 4 5
123 -456 777

因此,可以通过将vals(1:n)中的值复制到所需的数组来保存每行的结果。

[附录(感谢@francescalus)]  在上面的代码中,数据一次读入buf(1:n),然后复制到val(1:n)。有人可能会认为将数据读入val(1:n)更直接,这样

read( line, *, end=100, err=100 ) val( 1 : n )

但是,不推荐这种直接方法,因为当read语句遇到“end”或“err”条件时,val(1:n)变为未定义。尽管ifort和gfortran似乎保留了val(1:n)中的数据,即使满足该条件(因此即使采用直接方法也能工作),但其他编译器无法保证相同的行为。相反,缓冲方法通过将数据保存前一步到val(1:n)来避免这种风险,因此不使用未定义的数据。这就是为什么缓冲区方法在上面的代码中使用,尽管它只有一个语句。

答案 1 :(得分:1)

这样的事情可能会满足您的要求

  INTEGER :: ix, rdstat
  INTEGER, DIMENSION(6) :: nums
  CHARACTER(len=128) :: aline
  ...
  OPEN(21,file='data.txt')

  DO ix = 1,10
     READ(21,'(a)',iostat=rdstat) aline
     IF (rdstat/=0) catch_errors()

     nums = -99 ! a guard
     READ(aline,*,iostat=rdstat) nums
     IF (rdstat/=0) catch_errors()
     ! do something with nums
  END DO

  CLOSE(21)

我没有彻底测试过这个,我没有为你写过catch_errors - 实际上你可能不想做太多。第一个版本可能太脆弱了,但它是否合适在很大程度上取决于输入文件的一致性(或其他)。

策略是将每一行读入一个字符变量(一个足够长的整行),然后使用内部的,列表导向的读取从字符变量的开头读取6个整数。这利用了内置工具,列表导向输入在输入流中找到整数,空格分隔值。该方法应该与逗号分隔的整数列表一起使用。内部只读查找6个整数,然后在找不到更多整数时捕获错误,或者只捕获不能解释为整数的材料(例如像# comment这样的字符串)。

请注意

  • 我假设最大行长度为128个字符,您可能需要调整它。
  • 我已经为程序读取的行数指定了固定的上限。您可能想要更改它,或更改为do/while循环。
  • 程序预计一行中不超过6个整数,正如您的问题所指定的那样。
  • 在每一行读取时,数组nums在每个元素处都填充-99;这是一个'后卫'。如果输入文件中可能出现-99,您可能需要更改此内容。
  • 完全取决于您使用nums中的数字后所做的事情。