在Fortran中读写表

时间:2016-02-12 16:38:34

标签: io fortran

我有一个日历表,其上升和设置时间为

   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

我不确定如何使用他们的解决方案。有人可以为这类问题发布一般格式吗?

1 个答案:

答案 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(虚拟字符串)来跳过已处理的数据。请注意,空字段的riseset设置为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,分为字段,然后提取整数值(如果有)。此处,空数据的riseset设置为用户定义的虚拟值(此处为-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这是最简单,最灵活的方式,只要我们可以修改输入文件。

[序列化数据]可以将riseset序列化为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选项)。