[英]Reading Fortran binary file in Python
我在用 Python 讀取未格式化的 F77 二進制文件時遇到問題。 我試過SciPy.io.FortraFile
方法和NumPy.fromfile
方法,都無濟於事。 我還閱讀了 IDL 中的文件,它有效,所以我有一個數據應該是什么樣子的基准。 我希望有人能指出我犯的一個愚蠢的錯誤——沒有什么比愚蠢的時刻然后洗手更好的了……
數據 bcube1 的維度為 101x101x101x3,類型為 r*8。 共有 3090903 個條目。 它們是使用以下語句編寫的(不是我的代碼,從源代碼復制而來)。
open (unit=21, file=bendnm, status='new'
. ,form='unformatted')
write (21) bcube1
close (unit=21)
我可以使用以下代碼在 IDL 中成功讀取它(也不是我的代碼,從同事那里復制的):
bcube=dblarr(101,101,101,3)
openr,lun,'bcube.0000000',/get_lun,/f77_unformatted,/swap_if_little_endian
readu,lun,bcube
free_lun,lun
返回的數據 (bcube) 是雙精度的,尺寸為 101x101x101x3,因此文件的標頭信息知道它的尺寸(不是展平)。
現在我嘗試使用 Python 獲得相同的效果,但沒有成功。 我試過以下方法。
In [30]: f = scipy.io.FortranFile('bcube.0000000', header_dtype='uint32')
In [31]: b = f.read_record(dtype='float64')
返回錯誤Size obtained (3092150529) is not a multiple of the dtypes given (8)
。 更改 dtype 會更改獲得的大小,但它仍然不能被 8 整除。
或者,使用fromfile
導致錯誤,但會返回數組中的另一個值(也許是頁腳?),並且各個數組值都大錯特錯(都應該統一順序)。
In [38]: f = np.fromfile('bcube.0000000')
In [39]: f.shape
Out[39]: (3090904,)
In [42]: f
Out[42]: array([ -3.09179121e-030, 4.97284231e-020, -1.06514594e+299, ...,
8.97359707e-029, 6.79921640e-316, -1.79102266e-037])
我試過使用 byteswap 來查看這是否使浮點值更合理,但事實並非如此。
在我看來, np.fromfile
方法非常接近工作,但它讀取標題信息的方式肯定有問題。 任何人都可以建議我如何弄清楚頭文件中應該包含哪些內容,以便讓 IDL 了解數組維度和數據類型? 有沒有辦法將標題信息傳遞給fromfile
以便它知道如何處理前導條目?
我玩了一下,我想我有了一個主意。
Fortran 存儲未格式化數據的方式並未標准化,因此您必須嘗試使用它,但您需要三個信息:
標頭的類型。 這是一個無符號整數,但您需要以字節為單位的長度。 如果不確定,請嘗試 4。
頭部通常以字節為單位存儲記錄的長度,並在末尾重復。
再一次,它不是標准化的,所以沒有保證。
字節順序,小或大。
從技術上講,對於標頭和值,但我認為它們是相同的。
Python 默認為 little endian,所以如果那是您的數據的正確設置,我想您已經解決了它。
當您使用scipy.io.FortranFile
打開文件時,您需要提供文件頭的數據類型。 因此,如果數據存儲為 big_endian,並且您有一個 4 字節無符號整數標頭,您需要這樣:
from scipy.io import FortranFile
ff = FortranFile('data.dat', 'r', '>u4')
讀取數據時,您需要值的數據類型。 同樣,假設 big_endian,您需要鍵入>f8
:
vals = ff.read_reals('>f8')
在此處查看數據類型語法的描述。
如果您可以控制寫入數據的程序,我強烈建議您將它們寫入數據流中,Python 可以更輕松地讀取它們。
Fortran 的記錄分界很少被記錄,即使在二進制文件中也是如此。
所以每次寫入未格式化的文件:
integer*4 Test1
real*4 Matrix(3,3)
open(78,format='unformatted')
write(78) Test1
write(78) Matrix
close(78)
最終應由 np.int32 值填充。 (我已經看到這會告訴您記錄長度的參考資料,但尚未親自驗證。)
上面的內容可以通過 numpy 在 Python 中讀取為:
input_file = open(file_location,'rb')
datum = np.dtype([('P1',np.int32),('Test1',np.int32),('P2',np.int32),('P3',mp.int32),('MatrixT',(np.float32,(3,3))),('P4',np.int32)])
data = np.fromfile(input_file,datum)
這應該用上述格式的各個數據集完全填充數據數組。 請注意,numpy 期望數據以 C 格式(行優先)打包,而 Fortran 格式數據是列優先。 對於像上面那樣的方陣形狀,這意味着在使用之前從矩陣中獲取數據也需要轉置。 對於非方陣,您需要重塑和轉置:
Matrix = np.transpose(data[0]['MatrixT']
轉置您的 4-D 數據結構需要小心完成。 您可能會研究 SciPy 以尋找自動化的方法; SciPy 包似乎有 Fortran 相關的實用程序,我還沒有完全探索過。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.