在 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
我的问题:
在 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 做 "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
缩短的可能性:使用 equivalence
或 transfer
将 str
视为字符数组,并使用 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
现在你可以用这两个数组做任何你想做的事,因为你知道所有的数据,它来自哪一行,每行有多少数据。
我想用 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
我的问题:
在 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 做 "real" 计算的事情。但是,我想在文件 I/O.
我不是说它是最短的,但它比你的短得多。一旦拥有它,您就可以重复使用它。我不完全同意这些关于 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
缩短的可能性:使用 equivalence
或 transfer
将 str
视为字符数组,并使用 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
现在你可以用这两个数组做任何你想做的事,因为你知道所有的数据,它来自哪一行,每行有多少数据。