Fortran 读取混合文本和数字
Fortran read mixed text and numbers
我正在使用 Fortran 90 读取包含以下格式数据的文件
number# 125 var1= 2 var2= 1 var3: 4
.
.
.
.
number# 234 var1= 3 var2= 5 var3: 1
我尝试了以下命令并且工作正常
read (2,*) tempstr , my_param(1), tempstr , my_param(2), tempstr , my_param(3)
问题是当数字变大并且字符串和数字之间没有space时,即数据如下所示:
number# 125 var1= 2 var2=124 var3: 4
我试过了
read (2,512) my_param(1), my_param(2), my_param(3)
512 format('number#', i, 'var1=', i, 'var2=', i, 'var3:', i)
它将所有数字读为零
我无法切换到其他语言。数据集很大,所以我无法对其进行预处理。此外,分隔符每次都不相同。
有人可以帮忙解决这个问题吗?
提前致谢
首先,72万行对于预处理来说并不算多。 sed
和 awk
等工具主要在逐行的基础上工作,因此它们的扩展性非常好。
我实际上所做的是以我可以使用名单的方式转换数据:
$ cat preprocess.sed
# Add commas between values
# Space followed by letter -> insert comma
s/ \([[:alpha:]]\)/ , /g
# "number" is a key word in Fortran, so replace it with num
s/number/num/g
# Replace all possible data delimitors with the equals character
s/[#:]/=/g
# add the '&mydata' namelist descriptor to the beginning
s/^/\&mydata /1
# add the namelist closing "/" character to the end of the line:
s,$,/,1
$ sed -f preprocess.sed < data.dat > data.nml
检查数据是否已正确预处理:
$ tail -3 data.dat
number#1997 var1=114 var2=130 var3:127
number#1998 var1=164 var2=192 var3: 86
number#1999 var1=101 var2= 48 var3:120
$ tail -3 data.nml
&mydata num=1997 , var1=114 , var2=130 , var3=127/
&mydata num=1998 , var1=164 , var2=192 , var3= 86/
&mydata num=1999 , var1=101 , var2= 48 , var3=120/
然后你可以用这个 fortran 程序阅读它:
program read_mixed
implicit none
integer :: num, var1, var2, var3
integer :: io_stat
namelist /mydata/ num, var1, var2, var3
open(unit=100, file='data.nml', status='old', action='read')
do
read(100, nml=mydata, iostat=io_stat)
if (io_stat /= 0) exit
print *, num, var1, var2, var3
end do
close(100)
end program read_mixed
虽然我仍然坚持我原来的答案,特别是因为输入数据已经非常接近名单文件的样子,但我们假设您真的无法事先对数据进行任何预处理。
下一个最好的办法是将整行读入一个 character(len=<enough>)
变量,然后使用字符串操作从中提取值。像这样:
program mixed2
implicit none
integer :: num, val1, val2, val3
character(len=50) :: line
integer :: io_stat
open(unit=100, file='data.dat', action='READ', status='OLD')
do
read(100, '(A)', iostat=io_stat) line
if (io_stat /= 0) exit
call get_values(line, num, val1, val2, val3)
print *, num, val1, val2, val3
end do
close(100)
contains
subroutine get_values(line, n, v1, v2, v3)
implicit none
character(len=*), intent(in) :: line
integer, intent(out) :: n, v1, v2, v3
integer :: idx
! Search for "number#"
idx = index(line, 'number#') + len('number#')
! Get the integer after that word
read(line(idx:idx+3), '(I4)') n
idx = index(line, 'var1') + len('var1=')
read(line(idx:idx+3), '(I4)') v1
idx = index(line, 'var2') + len('var3=')
read(line(idx:idx+3), '(I4)') v2
idx = index(line, 'var3') + len('var3:')
read(line(idx:idx+3), '(I4)') v3
end subroutine get_values
end program mixed2
请注意,我没有包含任何 error/sanity 检查。我会把它留给你。
我正在使用 Fortran 90 读取包含以下格式数据的文件
number# 125 var1= 2 var2= 1 var3: 4
.
.
.
.
number# 234 var1= 3 var2= 5 var3: 1
我尝试了以下命令并且工作正常
read (2,*) tempstr , my_param(1), tempstr , my_param(2), tempstr , my_param(3)
问题是当数字变大并且字符串和数字之间没有space时,即数据如下所示:
number# 125 var1= 2 var2=124 var3: 4
我试过了
read (2,512) my_param(1), my_param(2), my_param(3)
512 format('number#', i, 'var1=', i, 'var2=', i, 'var3:', i)
它将所有数字读为零
我无法切换到其他语言。数据集很大,所以我无法对其进行预处理。此外,分隔符每次都不相同。 有人可以帮忙解决这个问题吗?
提前致谢
首先,72万行对于预处理来说并不算多。 sed
和 awk
等工具主要在逐行的基础上工作,因此它们的扩展性非常好。
我实际上所做的是以我可以使用名单的方式转换数据:
$ cat preprocess.sed
# Add commas between values
# Space followed by letter -> insert comma
s/ \([[:alpha:]]\)/ , /g
# "number" is a key word in Fortran, so replace it with num
s/number/num/g
# Replace all possible data delimitors with the equals character
s/[#:]/=/g
# add the '&mydata' namelist descriptor to the beginning
s/^/\&mydata /1
# add the namelist closing "/" character to the end of the line:
s,$,/,1
$ sed -f preprocess.sed < data.dat > data.nml
检查数据是否已正确预处理:
$ tail -3 data.dat
number#1997 var1=114 var2=130 var3:127
number#1998 var1=164 var2=192 var3: 86
number#1999 var1=101 var2= 48 var3:120
$ tail -3 data.nml
&mydata num=1997 , var1=114 , var2=130 , var3=127/
&mydata num=1998 , var1=164 , var2=192 , var3= 86/
&mydata num=1999 , var1=101 , var2= 48 , var3=120/
然后你可以用这个 fortran 程序阅读它:
program read_mixed
implicit none
integer :: num, var1, var2, var3
integer :: io_stat
namelist /mydata/ num, var1, var2, var3
open(unit=100, file='data.nml', status='old', action='read')
do
read(100, nml=mydata, iostat=io_stat)
if (io_stat /= 0) exit
print *, num, var1, var2, var3
end do
close(100)
end program read_mixed
虽然我仍然坚持我原来的答案,特别是因为输入数据已经非常接近名单文件的样子,但我们假设您真的无法事先对数据进行任何预处理。
下一个最好的办法是将整行读入一个 character(len=<enough>)
变量,然后使用字符串操作从中提取值。像这样:
program mixed2
implicit none
integer :: num, val1, val2, val3
character(len=50) :: line
integer :: io_stat
open(unit=100, file='data.dat', action='READ', status='OLD')
do
read(100, '(A)', iostat=io_stat) line
if (io_stat /= 0) exit
call get_values(line, num, val1, val2, val3)
print *, num, val1, val2, val3
end do
close(100)
contains
subroutine get_values(line, n, v1, v2, v3)
implicit none
character(len=*), intent(in) :: line
integer, intent(out) :: n, v1, v2, v3
integer :: idx
! Search for "number#"
idx = index(line, 'number#') + len('number#')
! Get the integer after that word
read(line(idx:idx+3), '(I4)') n
idx = index(line, 'var1') + len('var1=')
read(line(idx:idx+3), '(I4)') v1
idx = index(line, 'var2') + len('var3=')
read(line(idx:idx+3), '(I4)') v2
idx = index(line, 'var3') + len('var3:')
read(line(idx:idx+3), '(I4)') v3
end subroutine get_values
end program mixed2
请注意,我没有包含任何 error/sanity 检查。我会把它留给你。