2

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

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

样本输入:

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

我的问题

  • 在 Fortran 中有没有更简单的方法来做到这一点?我的意思是,读取一个值列表,其中要读取的值的数量是未知的。上面的代码看起来很别扭,在 Fortran 中文件 I/O 看起来并不容易。

  • 此外,主程序必须读取长度未知且无限长的行。如果我假设它们都是相同的长度(见下文),我能够读取行,但我不知道如何读取无界行。我想它需要 Fortran 2003 的流功能,但我不知道怎么写。

这是当前的程序:

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 做“真正的”计算工作。但是,我想提高我在文件 I/O 方面的 Fortran 技能。

4

2 回答 2

4

我并不是说它是最短的,但它比你的短得多。一旦你拥有它,你就可以重复使用它。我不完全同意这些说法,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

缩短的可能性:使用or将str视为字符数组,并使用inside of来获取.equivalencetransfercount()allocatea

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

    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
于 2015-05-02T21:07:20.907 回答
0

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

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

如果你想消除零值,你可以删除它们,或者如果你不期望任何值,你可以用负数预先给“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

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

于 2016-01-04T04:56:43.147 回答