簡體   English   中英

在 Python 中讀取 Fortran 二進制文件

[英]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 存儲未格式化數據的方式並未標准化,因此您必須嘗試使用它,但您需要三個信息:

  1. 數據的格式。 您建議這是 64 位實數,或 python 中的“f8”。
  2. 標頭的類型。 這是一個無符號整數,但您需要以字節為單位的長度。 如果不確定,請嘗試 4。

    頭部通常以字節為單位存儲記錄的長度,並在末尾重復。

    再一次,它不是標准化的,所以沒有保證。

  3. 字節順序,小或大。

    從技術上講,對於標頭和值,但我認為它們是相同的。

    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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM