在 Fortran 中读取具有未知边界的行中的整数序列
Reading a sequence of integer in a line with unknown bound in fortran
我想在 FORTRAN 中读取未知边界行中的整数序列。我的问题和之前的post、
类似
但是我想在一行中读取一系列未知数的整数并将其保存在单独的数组中。连续的整数行应该保存到其他数组
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 数组中)。我有数百行这样的,所以我需要根据最大长度进行分类并计算它们。
可能有很多方法可以做到这一点,下面就是一个这样的例子。这里,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 )
但是,不推荐这种直接方法,因为当读取语句遇到“结束”或“错误”条件时,vals(1:n)
变为未定义。尽管 ifort 和 gfortran 似乎保留 vals(1:n)
中的数据,即使满足该条件(因此它们甚至可以使用直接方法),但不能保证其他编译器具有相同的行为。相比之下,缓冲区方法通过将前一步的数据保存到 vals(1:n)
来避免这种风险,因此不会使用未定义的数据。这就是为什么在上面的代码中使用缓冲区方法的原因,尽管它比一条语句长。
这样的东西可能会满足您的要求
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 个整数,然后在找不到更多整数或仅 material 不能解释为整数时捕获错误(例如像 # comment
这样的字符串)。
备注
- 我假设最大行长度为 128 个字符,您可能需要调整它。
- 我已经为程序读取的行数指定了一个固定的上限。您可能想要更改它,或更改为
do/while
循环。
- 如您的问题所述,程序要求一行中的整数不超过 6 个。
- 在每一行读取数组
nums
并在每个元素处填充 -99
;这是一个'guard'。如果 -99
可能出现在您的输入文件中,您可能需要更改它。
- 一旦你拥有
nums
中的数字,你将如何处理它们完全取决于你。
我想在 FORTRAN 中读取未知边界行中的整数序列。我的问题和之前的post、
类似但是我想在一行中读取一系列未知数的整数并将其保存在单独的数组中。连续的整数行应该保存到其他数组
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 数组中)。我有数百行这样的,所以我需要根据最大长度进行分类并计算它们。
可能有很多方法可以做到这一点,下面就是一个这样的例子。这里,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 )
但是,不推荐这种直接方法,因为当读取语句遇到“结束”或“错误”条件时,vals(1:n)
变为未定义。尽管 ifort 和 gfortran 似乎保留 vals(1:n)
中的数据,即使满足该条件(因此它们甚至可以使用直接方法),但不能保证其他编译器具有相同的行为。相比之下,缓冲区方法通过将前一步的数据保存到 vals(1:n)
来避免这种风险,因此不会使用未定义的数据。这就是为什么在上面的代码中使用缓冲区方法的原因,尽管它比一条语句长。
这样的东西可能会满足您的要求
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 个整数,然后在找不到更多整数或仅 material 不能解释为整数时捕获错误(例如像 # comment
这样的字符串)。
备注
- 我假设最大行长度为 128 个字符,您可能需要调整它。
- 我已经为程序读取的行数指定了一个固定的上限。您可能想要更改它,或更改为
do/while
循环。 - 如您的问题所述,程序要求一行中的整数不超过 6 个。
- 在每一行读取数组
nums
并在每个元素处填充-99
;这是一个'guard'。如果-99
可能出现在您的输入文件中,您可能需要更改它。 - 一旦你拥有
nums
中的数字,你将如何处理它们完全取决于你。