【问题标题】:Reading a sequence of integer in a line with unknown bound in fortran在fortran中读取具有未知界限的行中的整数序列
【发布时间】:2015-08-15 06:02:16
【问题描述】:

我想在 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

【问题讨论】:

    标签: fortran line


    【解决方案1】:

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

    subroutine split( line, vals, n )
        implicit none
        character(*), intent(in) :: line
        real*8  :: vals(*), buf( 100 )  !! currently up to 100 items (for test)
        integer :: n
    
        n = 1
        do
            read( line, *, end=100, err=100 ) buf( 1 : n )   !! (See Appendix for why buf is used here)
            vals( 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( 100 )
        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),然后复制到vals(1:n)。有人可能会认为将数据读入vals(1:n) 会更直接,这样

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

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

    【讨论】:

    • 您好,我尝试使用 gfortran 将您的代码编译为 .f90 文件,它导致 read-test.f90:10.8: val( 1:n ) = buf( 1:n ) 1 Error: Unclassifiable statement at (1
    • @AlphaF20 非常感谢!我相信valvals 的错字(所以现在在上面的代码中更正了)。此外,我将buf 的大小从 10000 更改为 100(因为 gfortran 发出了关于大型本地数组的警告)。
    【解决方案2】:

    这样的东西可能会满足您的要求

      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 中的数字后,如何处理这些数字完全取决于您。

    【讨论】:

      猜你喜欢
      • 2017-10-30
      • 1970-01-01
      • 2021-09-14
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      • 2016-06-22
      • 2013-02-12
      • 1970-01-01
      相关资源
      最近更新 更多