我正在尝试用python读取一个非常大的Fortran无格式二进制文件。该文件包含2 ^ 30个整数。
我发现记录标记令人困惑(第一个是-2147483639),无论如何我已经实现了恢复数据结构(那些想要的整数都是相似的,因此与记录标记不同)并编写下面的代码(带有here的帮助。
但是,我们可以看到每条记录开头和结尾的标记不一样。这是为什么?
是因为数据的大小太长(536870910 =(2 ^ 30 - 4)/ 2)? 但是(2 ^ 31-1)/ 4 = 536870911> 536870910。
或者只是数据文件的作者所犯的一些错误?
另一个问题,记录,int或unsigned int开头的标记类型是什么?
fp = open(file_path, "rb")
rec_len1, = struct.unpack( '>i', fp.read(4) )
data1 = np.fromfile( fp, '>i', 536870910)
rec_end1, = struct.unpack( '>i', fp.read(4) )
rec_len2, = struct.unpack( '>i', fp.read(4) )
data2 = np.fromfile( fp, '>i', 536870910)
rec_end2, = struct.unpack( '>i', fp.read(4) )
rec_len3, = struct.unpack( '>i', fp.read(4) )
data3 = np.fromfile( fp, '>i', 4)
rec_end3, = struct.unpack( '>i', fp.read(4) )
data = np.concatenate([data1, data2, data3])
(rec_len1,rec_end1,rec_len2,rec_end2,rec_len3,rec_end3)
这里记录的lenth值如上所示:
(-2147483639, -2176, 2406, 589824, 1227787, -18)
答案 0 :(得分:2)
最后,事情似乎更清楚了。
Here是英特尔Fortran编译器用户和参考指南, 请参阅记录类型:可变长度记录部分。
对于大于2,147,483,639字节的记录长度,记录为 分为子记录。子记录可以是从1到1的任何长度 2,147,483,639,包括在内。
前导长度字段的符号位表示是否记录 继续与否。尾随长度字段的符号位 表示存在前一个子记录。的位置 符号位由文件的endian格式决定。
继续的子记录具有带符号的前导长度字段 比特值1.构成记录的最后一个子记录有一个 符号位值为0的前导长度字段。具有的子记录 前面的子记录具有带符号位的尾随长度字段 值1.构成记录的第一个子记录有一个尾随 长度字段,符号位值为0.如果符号位的值 为1时,记录的长度存储在二进制补码表示法中。
经过多篇论文后,我意识到我被二进制补码表示法误导了,记录标记只是按照上面的规则改变符号,而是改为二进制补码表示法当符号位为1.无论如何,我的数据也可能是 用不同的编译器创建。
以下是解决方案。
数据大于2GB,因此它分为几个子记录。 我们看到第一个记录开始标记是-2147483639, 所以第一个记录的长度是2147483639,这正是子记录的最大长度,而不是我认为的2147483640,也不是2147483638的-2147483639的二进制补码表示法。
如果我们跳过2147483639字节来读取记录结束标记,您将获得2147483639, 因为它是第一个结束标记为正的子记录。
以下是检查记录标记的代码:
fp = open(file_path, "rb")
while 1:
prefix, = struct.unpack( '>i', fp.read(4) )
fp.seek(abs(prefix), 1) #or read |prefix| bytes data as you want
suffix, = struct.unpack( '>i', fp.read(4) )
print prefix, suffix
if abs(suffix) - abs(prefix):
print "suffix != prefix!"
break
if prefix > 0: break
并进行丝网印刷
-2147483639 2147483639
-2147483639 -2147483639
18 -18
除了符号,我们可以看到记录开始标记和结束标记始终相同。 三条记录的长度分别为2147483639,2147483639,18字节,不必是4的倍数。因此第一条记录以某个整数的前3个字节结束,第二条记录以其余1字节开始。
答案 1 :(得分:0)
我发现使用f2py是python访问fortran数据的一种更方便的方法。 然而,记录标记的奇怪行为仍然是一个问题。至少我们可以避免潜入(有时令人困惑)fortran无格式文件结构。它与numpy匹配得很好。
F2PY用户指南和参考手册为here。 这是一个示例fortran源文件,用于打开和关闭文件,读取整数1-D数组和浮动2-D数组。请注意,评论以!f2py开头,它们有助于使f2py更“聪明”。
要使用它,您需要将其包装到模块中并导入到python会话中。然后你就像那些python函数一样调用这些函数。
!ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
!cc cc
!cc FORTRAN MODULE for PYTHON PROGRAM CALLING cc
!cc cc
!ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
!Usage:
! Compile: f2py -c fortio.f90 -m fortio
! Import: from fortio import *
! or import fortio
!Note:
! Big endian: 1; Little endian: 0
!cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
SUBROUTINE open_fortran_file(fileUnit, fileName, endian, error)
implicit none
character(len=256) :: fileName
integer*4 :: fileUnit, error, endian
!f2py integer*4 optional, intent(in) :: endian=1
!f2py integer*4 intent(out) :: error
if(endian .NE. 0) then
open(unit=fileUnit, FILE=fileName, form='unformatted', status='old', &
iostat=error, convert='big_endian')
else
open(unit=fileUnit, FILE=fileName, form='unformatted', status='old', &
iostat=error)
endif
END SUBROUTINE
!cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
SUBROUTINE read_fortran_integer4(fileUnit, arr, leng)
implicit none
integer*4 :: fileUnit, leng
integer*4 :: arr(leng)
!f2py integer*4 intent(in) :: fileUnit, leng
!f2py integer*4 intent(out), dimension(leng), depend(leng) :: arr(leng)
read(fileUnit) arr
END SUBROUTINE
!cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
SUBROUTINE read_fortran_real4(fileUnit, arr, row, col)
implicit none
integer*4 :: fileUnit, row, col
real*4 :: arr(row,col)
!f2py integer*4 intent(in):: fileUnit, row, col
!f2py real*4 intent(out), dimension(row, col), depend(row, col) :: arr(row,col)
read(fileUnit) arr
END SUBROUTINE
!cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
SUBROUTINE close_fortran_file(fileUnit, error)
implicit none
integer*4 :: fileUnit, error
!f2py integer*4 intent(in) :: fileUnit
!f2py integer*4 intent(out) :: error
close(fileUnit, iostat=error)
END SUBROUTINE
答案 2 :(得分:0)
由于这个问题似乎经常出现......这是一个python utilty代码,用于扫描二进制文件并确定它是否(可能是)fortran无格式的顺序访问文件。它通过尝试几种标题格式来工作。当然,由于“无格式”格式不是标准格式,因此可能存在其他变量,但这应该是最常见的格式。
请注意左括号已转义,因此您可能需要更改& #060;如果你屏幕复制这个,请回到“小于”的标志。
def scanfbinary(hformat,file,fsize):
""" scan a file to see if it has the simple structure typical of
an unformatted sequential access fortran binary:
recl1,<data of length recl1 bytes>,recl1,recl2,<data of length recl2 bytes>,recl2 ...
"""
import struct
print 'scan type',hformat,
if 'qQ'.find(hformat[1])>=0: hsize=8
elif 'iIlL'.find(hformat[1])>=0: hsize=4
if hformat[0] == '<': endian='little'
elif hformat[0] == '>': endian='big'
print '(',endian,'endian',hsize,'byte header)',
f.seek(0)
nrec = 0
while fsize > 0:
h0=struct.unpack(hformat,f.read(hsize))[0]
if h0 < 0 : print 'invalid integer ',h0; return 1
if h0 > fsize - 2*hsize:
print 'invalid header size ',h0,' exceeds file size ',fsize
if nrec > 0:print 'odd perhaps a corrupe file?'
return 2
# to read the data replace the next line with code to read h0 bytes..
# eg
# import numpy
# dtype = numpy.dtype('<i')
# record=numpy.fromfile(f,dtype,h0/dtype.itemsize)
f.seek(h0,1)
h=struct.unpack(hformat,f.read(hsize))[0]
if h0!=h : print 'unmatched header'; return 3
nrec+=1
if nrec == 1:print
if nrec < 10:print 'read record',nrec,'size',h
fsize-=(h+2*hsize)
print 'successfully read ',nrec,' records with unformatted fortran header type',hformat
return 0
f=open('binaryfilename','r')
f.seek(0,2)
fsize=f.tell()
res=[scanfbinary(hformat,f,fsize) for hformat in ('<q','>q','<i','>i')]
if res.count(0)==0:
print 'no match found, file size ',fsize, 'starts..'
f.seek(0)
for i in range(0,12): print f.read(2).encode('hex_codec'),
print