[英]Reading and Writing Tables in 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,以后再處理。2月底的空白表示該日期不存在。)
顯然,我不想一次將整個年度加載到一個數組中(試圖在任何給定的時間段確定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
類似的問題在這里: 可變格式
我不確定如何使用他們的解決方案。 有人可以針對這種問題發布通用格式嗎?
有幾種可能的方法,下面我將介紹三種模式。
[ 第一種方法 ]正如課題中所建議的那樣,人們一次讀取兩列。 假設每年有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
and set
for empty field設置為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
)。 也可以使用類似的方法來改進第一種方法。
[ 第三種方法 ]有時使用自由格式的輸入文件更為方便。 為此,讓我們用空值填充空數據,例如-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 )
(請注意,對於Intel fortran,我們需要-assume realloc_lhs
選項)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.