在 Fortran 中读取整数列表文件

Reading a file of lists of integers in Fortran

我想用 Fortran 程序读取数据文件,其中每一行都是一个整数列表。

每一行都有数量可变的整数,由给定字符(space、逗号...)分隔。

示例输入:

1,7,3,2
2,8
12,44,13,11

我有一个拆分线的解决方案,我觉得很复杂:

module split
    implicit none
contains
    function string_to_integers(str, sep) result(a)
        integer, allocatable :: a(:)
        integer :: i, j, k, n, m, p, r
        character(*) :: str
        character :: sep, c
        character(:), allocatable :: tmp

        !First pass: find number of items (m), and maximum length of an item (r)
        n = len_trim(str)
        m = 1
        j = 0
        r = 0
        do i = 1, n
            if(str(i:i) == sep) then
                m = m + 1
                r = max(r, j)
                j = 0
            else
                j = j + 1
            end if
        end do
        r = max(r, j)

        allocate(a(m))
        allocate(character(r) :: tmp)

        !Second pass: copy each item into temporary string (tmp),
        !read an integer from tmp, and write this integer in the output array (a)
        tmp(1:r) = " "
        j = 0
        k = 0
        do i = 1, n
            c = str(i:i)
            if(c == sep) then
                k = k + 1
                read(tmp, *) p
                a(k) = p
                tmp(1:r) = " "
                j = 0
            else
                j = j + 1
                tmp(j:j) = c
            end if
        end do
        k = k + 1
        read(tmp, *) p
        a(k) = p
        deallocate(tmp)
    end function
end module

我的问题:

这是当前节目:

program read_data
    use split
    implicit none
    integer :: q
    integer, allocatable :: a(:)
    character(80) :: line
    open(unit=10, file="input.txt", action="read", status="old", form="formatted")
    do
        read(10, "(A80)", iostat=q) line
        if(q /= 0) exit
        if(line(1:1) /= "#") then
            a = string_to_integers(line, ",")
            print *, ubound(a), a
        end if
    end do
    close(10)
end program

关于这个问题的评论:通常我会在Python中这样做,例如转换一行就像a = [int(x) for x in line.split(",")]一样简单,而读取文件同样几乎是一件微不足道的工作。我会用 Fortran DLL 做 "real" 计算的事情。但是,我想在文件 I/O.

上提高我的 Fortran 技能

我不是说它是最短的,但它比你的短得多。一旦拥有它,您就可以重复使用它。我不完全同意这些关于 Fotran 在字符串处理方面表现不佳的说法,我在 Fortran 中做标记化、递归下降解析和类似的东西就很好,尽管在其他一些具有更丰富的库的语言中更容易。有时您也可以在 Fortran 中使用其他语言(尤其是 C 和 C++)编写的库。

如果你总是使用逗号,你可以删除逗号替换,从而进一步缩短它。

function string_to_integers(str, sep) result(a)
    integer, allocatable :: a(:)
    character(*) :: str
    character :: sep
    integer :: i, n_sep

    n_sep = 0
    do i = 1, len_trim(str)
      if (str(i:i)==sep) then
        n_sep = n_sep + 1
        str(i:i) = ','
       end if
    end do
    allocate(a(n_sep+1))
    read(str,*) a
end function

缩短的可能性:使用 equivalencetransferstr 视为字符数组,并使用 allocate 内部的 count() 来获取大小a.

代码假定每个数字之间只有一个分隔符,并且在第一个数字之前没有分隔符。如果两个数字之间允许有多个分隔符,则必须检查前面的字符是否为分隔符

    do i = 2, len_trim(str)
      if (str(i:i)==sep .and. str(i-1:i-1)/=sep) then
        n_sep = n_sep + 1
        str(i:i) = ','
       end if
    end do

我的回答对于您的目标来说可能过于简单,但我最近花了很多时间阅读奇怪的数字文本文件。我最大的问题是找到他们从哪里开始(对你来说并不难)然后我最好的朋友是 list-directed 阅读。

read(unit=10,fmt=*) a

会将所有数据读入矢量 'a',完成处理。使用这种方法,您将不知道任何数据来自哪一行。如果你想分配它,那么你可以读取文件一次并找出一些算法来使数组比它需要的更大,比如计算行数并且你知道每行的最大数据量(比如 21)。

    status = 0
    do while ( status == 0)
      line_counter = line_counter + 1
      read(unit=10,, iostat=status, fmt=*)
    end do

allocate(a(counter*21))

如果你想消除零值,你可以删除它们,或者 pre-seed 带有负数的 'a' 向量,如果你不希望有任何值然后删除所有这些。

来自另一个建议的另一种方法是首先计算逗号,然后在循环由

控制的地方进行读取
do j = 1, line_counter         ! You determined this on your first read
  read(unit=11,fmt=*) a(j,:)   ! a is now a 2 dimensional array (line_counter, maxNumberPerLine)
                               ! You have a separate vector numberOfCommas(j) from before
end do

现在你可以用这两个数组做任何你想做的事,因为你知道所有的数据,它来自哪一行,每行有多少数据。