Fortran 77从旧太阳机读取未格式化的序列数据

时间:2014-01-14 15:40:46

标签: linux fortran gfortran fortran77 sun

我正在将一个旧的数学模型(1995年到2000年之间)移植到当前的linux机器上。为此,我调整了所有的makefile,如下所示:

FORTRAN  = gfortran # f90 -f77 -ftrap=%none
OPTS     = -O -u -lgfortran -g -fconvert="big-endian" # -O -u
NOOPT    = 
LOADER   = gfortran #f90
LOADOPTS = #-lf77compat

SYSFFLAGS   = -O0 -u -g -fconvert="big-endian" # -f77=input
SYSCFLAGS   = -DX_WCHAR
SYSLDFLAGS  =  
SYSCPPFLAGS = -DSYS_UNIX -DCODE_ASCII -DCODE_IEEE # -DSYS_Sun
SYSAUTODBL  = -fdefault-real-8 #-r8
SYSDEBUG    = -g
SYSCHECK    = -C 
LINKOPT =
CPPOPT  = 

SHELL       = /bin/sh
CC      = cc

FC      = gfortran # f90
LD      = gfortran # f90
AR      = ar vru
RM      = rm -f
CP      = cp
MV      = mv -f
LN      = ln -s

所以我替换了所有过时的编译器/选项,以便能够编译代码。之后,它生成没有错误的二进制文件。请注意,符号后面的所有选项都是Makefile中的原始选项。

因此,在运行程序时,无法读取示例数据。 IMO在Sun机器上将这些文件创建为无格式序列模式。以下十六进制转储属于我需要阅读的文件。

0000000: 0000 0400 2020 2020 2020 2020 2020 2020  ....
0000010: 3930 3130 7465 7374 2d63 3031 2020 2020  9010test-c01
0000020: 2020 2020 4741 5520 2020 2020 2020 2020      GAU
0000030: 2020 2020 2020 2020 2020 2020 2020 2020
0000040: 2020 2020 2020 2020 2020 2020 2020 2020
0000050: 2020 2020 2020 2020 2020 2020 2020 2020
...
...
0000390: 2020 2020 2020 2020 2020 2020 2020 2020
00003a0: 2020 2020 2020 2020 2020 2020 2020 2020
00003b0: 2020 2020 3139 3936 3037 3232 2032 3030      19960722 200
00003c0: 3434 3920 4147 434d 352e 3420 2020 2020  449 AGCM5.4
00003d0: 2020 2020 3230 3030 3036 3134 2031 3230      20000614 120
00003e0: 3831 3720 6869 726f 2020 2020 2020 2020  817 hiro
00003f0: 2020 2020 2020 2020 2020 2020 2020 2034                 4
0000400: 3039 3630 0000 0400 0002 8000 bef7 21f3  0960..........!.
0000410: bf3c 55ab bf7a 8f71 bf99 e26a bfb2 db4e  .<U..z.q...j...N
0000420: bfc7 425f bfd6 64b1 bfdf d44f bfe3 6a43  ..B_..d....O..jC

分析代码后,它可以读取直到 0000410 行。标记 0002 8000 后无法继续。下面显示的源代码实际上是读取此文件。

...
*   [INPUT] 
INTEGER    IFILE
CHARACTER  HITEM  *(*)                    !! name for identify
CHARACTER  HDFMT  *(*)                    !! data format
*
*   [ENTRY INPUT] 
REAL * 8   TIME1                          !! time
REAL * 8   TIME2                          !! time
REAL*8     DMIN
REAL*8     DMAX
REAL*8     DIVS
REAL*8     DIVL
INTEGER    ISTYPE
INTEGER    JFILE                          !! output file No.
INTEGER    IMAXD
INTEGER    JMAXD
*
*   [WORK] 
REAL * 8   DDATA ( NGDWRK )
REAL * 4   SDATA ( NGDWRK )
*
*   [INTERNAL WORK] 
INTEGER    I, J, K, IJK, IJKNUM, IERR
...
...
READ ( IFILE, IOSTAT=IEOD ) HEAD
...
...
...
DO 2150 IJK = 1, IJKNUM              
    READ ( IFILE, END=2150 ) SDATA(IJK)
    WRITE (6,*) ' IGTIO::GTZZRD: iteration=', IJK, SDATA(IJK)
2150 CONTINUE

为了轻松调试我替换上面的循环。原来是隐含的。

READ ( IFILE, IOSTAT=IEOD)
&              (SDATA(IJK), IJK=1, IJKNUM )

循环的输出是:

IGTIO::GTZZRD: iteration=           1 -0.48268089    
IGTIO::GTZZRD: iteration=           2  1.35631564E-19
IGTIO::GTZZRD: iteration=           3 -0.48142704    
IGTIO::GTZZRD: iteration=           4  1.35631564E-19
IGTIO::GTZZRD: iteration=           5   244.25270    
IGTIO::GTZZRD: iteration=           6  1.35631564E-19
IGTIO::GTZZRD: iteration=           7   983.87988    
IGTIO::GTZZRD: iteration=           8  1.35631564E-19
IGTIO::GTZZRD: iteration=           9  1.59284362E-04
IGTIO::GTZZRD: iteration=          10  1.35631564E-19
IGTIO::GTZZRD: iteration=          11   0.0000000  
---error here---

我绝对迷失在此,所以任何帮助都表示赞赏。

3 个答案:

答案 0 :(得分:2)

以下是最新消息 - 首先,这绝对是一个Big Enfian文件。 前4个字节

00000400

是大端4字节整数1024,这是您第一条记录的长度。 这符合HEAD的长度(每条评论) 现在请注意00000400在字节位置1024 + 4处完全重复(十六进制转储行400),正如您对fortran无格式文件所期望的那样......到目前为止一直很好。

现在接下来的4个字节

0002 8000

开始第二条记录。 (编辑纠正错误)这是163840(2 * 16 ^ 4 + 8 * 16 ^ 3)您应该在十六进制转储中找到重复的位置1024 + 8 + 163840 + 4。 (我认为应该是028400行。)

问题出现了:在你的代码中,你正在将160千字节的记录读入一个4字节的变量,然后转到下一条记录。我猜您会看到交替10 ^ -19,因为每个其他记录都是类型字符。

在未格式化的fortran中,您必须一次性读取整个记录 - 尝试读取整个数组(没有循环..)

READ ( IFILE )SDATA

假设sdata的尺寸为当然保持160 kb。 (例如,真* 4(40960))

答案 1 :(得分:1)

您的问题的答案在我错过的编辑中。还要感谢乔治的算术 - 我没有费心去做。

我们可以有把握地说记录标题是正确的,并且您可以通过字节序转换来解决问题。

所以,问题是:使用隐含do循环的读取不等同于do循环内的读取。

即:read(unit) (i(j), j=1,5)

不同
do j=1,5
  read(unit) i(j)
end do

首先,读取五个值来自一个记录,第二个记录是从不同记录中读取的。

然后,您应该还原您的更改。但是,如果要进行相同的诊断,可以执行类似

的操作
READ ( IFILE, IOSTAT=IEOD) (SDATA(IJK), IJK=1, IJKNUM )
WRITE (6, '("IGTIO::GTZZRD: iteration='", I0.0, F12.8)') (IJK, SDATA(IJK), IJK=1,IJKNUM)

答案 2 :(得分:0)

在此范围之外,由于某种原因,在1100循环中调用负责读取文件的深层方法。这导致读取文件的次数超过了必要的次数。在下面找到固定代码:

* 1100 CONTINUE 
     CALL   GDREDX         !! read data
 O         ( GDATA , IEOD  ,
 O           HITEMD, HTITL , HUNIT , HDSET , 
 O           TIME  , TDUR  , KLEVS ,
 I           IFILE , HITEM , HDFMT ,
 I           IMAXD , JMAXD , 
 I           IDIMD , JDIMD , KDIMD           )
     IF ( IEOD .EQ. 0  ) THEN
         WRITE (6,*) ' IRWGD.F::GDRDTS: TSEL0=', TSEL0
         WRITE (6,*) ' IRWGD.F::GDRDTS: TSEL1=', TSEL1
         WRITE (6,*) ' IRWGD.F::GDRDTS: TIME=', TIME
*            IF (    ((TSEL0.GE.0).AND.(TIME.LT.TSEL0)) 
*     &          .OR.((TSEL1.GE.0).AND.(TIME.GT.TSEL1)) ) THEN
*                     GOTO 1100
*            ENDIF
        ENDIF
*
  RETURN
  END

这是误导我,以弄清楚发生了什么。