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 でこれを行う簡単な方法はありますか? つまり、読み取る値の数が不明な値のリストを読み取ることです。上記のコードはぎこちなく見え、ファイル I/O は Fortran では簡単ではありません。

  • また、メイン プログラムは、不明で無制限の長さの行を読み取る必要があります。行がすべて同じ長さであると仮定すると、行を読み取ることができますが (以下を参照)、無制限の行を読み取る方法がわかりません。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 で問題なく実行できますが、より豊富なライブラリを備えた他の言語の方が簡単です。他の言語 (特に C と C++) で書かれたライブラリを Fortran でも使用できる場合があります。

常にコンマを使用する場合は、コンマによる置換を削除して、さらに短くすることができます。

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

短縮の可能性:またはを使用strして を文字配列として表示し、 の内部でを使用して のサイズを取得します。equivalencetransfercount()allocatea

このコードは、各数値の間に区切り記号が 1 つだけあり、最初の数値の前に区切り記号がないことを前提としています。2 つの数字の間に複数の区切り文字が許可されている場合は、前の文字が区切り文字かどうかを確認する必要があります。

    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

これで、すべてのデータ、それがどの行から来たのか、各行にいくつのデータがあったかがわかったので、これら 2 つの配列を使って好きなことを行うことができます。

于 2016-01-04T04:56:43.147 に答える