2014-01-14 31 views
0

我將一箇舊的數學模型(1995年至2000年之間)移植到當前的linux機器上。對於這一點,我適應所有的makefile文件,如下所示:Fortran 77從舊的sun機讀取無格式的序列數據

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 

所以我更換了所有過時的編譯器/選項能夠編譯代碼。之後,它會生成沒有錯誤的二進制文件。請注意,後面的符號的所有選項都在Makefiles中的原始選項中。

因此,在運行程序時,不可能讀取樣本數據。 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 

分析代碼之後,就能夠讀取,直到行。在標記之後不可能繼續。下面顯示的源代碼實際上是讀取這個文件。

... 
* [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--- 

我肯定失去了這一點,所以任何幫助表示讚賞。

+0

迭代2,'1.35631564E-19'仍然看起來對我來說是不正確的。至少在一點點。幾個想法探索。你能夠'ntohl'這些迭代。 SDATA的大小是多少,「real * 4」,你可以試試真實的* 8嗎?只是想法,祝你好運。 –

+0

你知道輸出應該是什麼嗎?初始輸出是否正確? Sun可能使用了big-endian,而如果使用英特爾,則Linux將是小端。對於原生格式的文件(Fortran未格式化),重要的是硬件而不是操作系統。轉換endian應該足夠整數。 gfortran有一個endian轉換的選項:http://gcc.gnu.org/onlinedocs/gfortran/CONVERT-specifier.html。浮點數可能不夠,這可能在表示方面有其他差異。 –

+0

@francescalus HEAD長度爲1024(64 * 16) – rgrun

回答

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 

誤導我,以便弄清楚發生了什麼事情。

2

這是怎麼回事 - 首先這肯定是一個大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的當然。 (例如real * 4(40960))

+0

ijknum的值是40960.SDATA的值是279040.你確定00028000是10240嗎?我認爲它是163840;)正如你所建議的那樣,這個改變是在內部對READ語句進行21次8192個字節的讀取(strace確認)。之後,它會引發「Fortran運行時錯誤:I/O超過未格式化文件的記錄末尾」。 – rgrun

+0

你是對的,我下了零。點仍然需要閱讀整個記錄與一個單一的讀語句 – agentp

+0

編輯說明..修復了答案中的數字.. – agentp

1

您的問題的答案在於我錯過的編輯。還要感謝喬治的算術 - 我沒有打算這麼做。

我們可以放心地說,記錄標頭是正確的,你可以通過endian轉換來解決你的問題。

所以,問題是:帶有隱含做循環的讀取不等於讀循環內的讀取。

即: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) 
+0

你是對的!我將它放回原處,然後在對該範圍外的一些代碼進行評論之後開始工作。 – rgrun