我有一个日历表,其上升和设置时间为
Jan Feb .... Dec
rise set rise set .... rise set
0643 1754 0433 1305 .... 1256 0219
1057 2230 1038 9999 .... 1502 0151
0912 9999 1026 0139 .... 1559 0103
1147 0149 .... 1739 0130
(不要担心9999,我稍后会处理它们。二月底的空白表示那天不存在)
显然,不是在一个数组中加载整年(这在尝试找出任何给定时间段的DOY时会是一场噩梦),我想一次处理两列数据。尝试用如下格式的变量编写脚本:
26 format(<iskip>X,I2,3X,I2)
idoy=001
iskip=4
irise1=00
4 read(1,26,end=6,err=4)irise2,iset2
if (irise2.eq.iset2) go to 6 !if both blank, move to next column
7 irise1=irise2
iset1=iset2
irise2=99
iset2=99
idoy++
go to 4
6 iskip=iskip+11
irise1=irise2
iset1=iset2
irise2=99
iset2=99
idoy++
if (iskip.lt.128) go to 4
基本上,程序会一次读取两列,存储数据并跟踪一年中的某一天。
为了试验在格式中使用变量的想法,我创建了一个测试程序,它打印出一个10x24的值矩阵,这些值应该像DOYHH一样逐列:
integer idoy,ihour,iskip
idoy=1
ihour=0
iskip=0
do while (idoy.le.10)
do while (ihour.lt.24)
2 format(<iskip>X,I3,I2)
write(2,2)idoy,ihour
end do
iskip=iskip+6
end do
end
但f77和f95的编译器似乎对此非常不满意。有关如何使测试程序工作的任何建议?我可以从那里推断出这种技术。同样,测试程序写入矩阵底部然后在顶部重新启动非常重要。
来自F77的错误消息:
test.f: In program `MAIN__':
test.f:9:
2 format(<iskip>X,I3,I2)
^
Variable-expression FORMAT specifier at (^) -- unsupported
来自F95的错误消息:
test.f:9.16:
2 format(<iskip>X,I3,I2)
1
Error: Unexpected element '<' in format string at (1)
test.f:10.17:
write(2,2)idoy,ihour
1
Error: FORMAT label 2 at (1) not defined
此处类似问题:Variable format
我不确定如何使用他们的解决方案。有人可以为这类问题发布一般格式吗?
答案 0 :(得分:0)
有几种可能的方法,我将在下面介绍三种模式。
[第一种方法]按照问题中的建议,一次读取两列。给出每年4天3个月的样本输入,每次以'(I5)'
格式编写,
Jan Feb June
rise set rise set rise set
0643 1754 0433 1305 1256 0219
1057 2230 1038 9999 1502 0151
0912 9999 1559 0103
1147 0149
我们可以将它们视为
integer, parameter :: Nday = 4, Nmon = 3 !! for actual data, Nday = 31 and Nmon = 12
integer, dimension( Nday, Nmon ) :: rise, set
integer :: d, m
character(200) :: lineskip
open( 10, file="time_blank.dat", status="old" )
do m = 1, Nmon
rewind( 10 )
read( 10, * ); read( 10, * ) !! skip the two header lines
do d = 1, Nday
read( 10, "(a, 2i5)" ) &
lineskip( 1 : 10 * (m-1) ), rise( d, m ), set( d, m )
enddo
enddo
close( 10 )
在这里,我们使用lineskip
(虚拟字符串)来跳过已处理的数据。请注意,空字段的rise
和set
设置为0
,根据具体情况,这可能不合适。
[第二种方法]一行将每行读入一个字符串,然后解析它以获得整数数据。例如,
integer :: offset
character(1000) :: line
character(5) :: str1, str2
rise(:,:) = -1 ; set(:,:) = -1 !! first, initialize all data with dummy values
...
do d = 1, Nday
read( 10, "(a)" ) line
do m = 1, Nmon
offset = 10 * (m-1)
str1 = line( offset + 1 : offset + 5 )
str2 = line( offset + 6 : offset + 10 )
if ( str1 /= "" ) read( str1, * ) rise( d, m )
if ( str2 /= "" ) read( str2, * ) set ( d, m )
enddo
enddo
其中每行读入line
,分为字段,然后提取整数值(如果有)。此处,空数据的rise
和set
设置为用户定义的虚拟值(此处为-1
)。类似的方法也可用于改进第一种方法。
[第3种方法]拥有自由格式输入文件有时会更方便。为了实现这一点,让我们用虚拟值填充空数据,例如-1:
Jan Feb June
rise set rise set rise set
0643 1754 0433 1305 1256 0219
1057 2230 1038 9999 1502 0151
0912 9999 -1 -1 1559 0103
1147 0149 -1 -1 -1 -1 <--- free format
然后我们可以一次读取所有数据
open( 10, file="time_filled.dat", status="old" )
read( 10, * ) !! skip the header
read( 10, * )
read( 10, * ) ( ( rise( d, m ), set( d, m ), m=1,Nmon ), d=1,Nday )
close( 10 )
IMO这是最简单,最灵活的方式,只要我们可以修改输入文件。
[序列化数据]可以将rise
和set
序列化为DOY的函数
idoy = 0
do m = 1, Nmon
do d = 1, Nday
if ( rise( d, m ) >= 0 ) then !! assuming the dummy value = -1
idoy = idoy + 1
print *, idoy, rise( d, m ), set( d, m )
endif
enddo
enddo
或使用pack
功能:
integer, allocatable :: rise_doy(:), set_doy(:)
rise_doy = pack( rise, rise >= 0 ) !! assuming the dummy value = -1
set_doy = pack( set, set >= 0 )
(请注意,对于英特尔Fortran,我们需要-assume realloc_lhs
选项)。