2

非常に大きなFortranのフォーマットされていないバイナリファイルをPythonで読み込もうとしています。このファイルには2^30の整数が含まれています。

レコードマーカーが混乱していることがわかりました(最初のものは-2147483639です)、とにかくデータ構造を回復することを達成しました(これらの必要な整数はすべて類似しているため、レコードマーカーとは異なります)、以下のコードを記述します(ここの助けを借りて) )。

ただし、各レコードの最初と最後のマーカーが同じではないことがわかります。何故ですか?

データのサイズが長すぎるためですか(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)

上記のように読み取られたレコード長の値は次のとおりです。

(-2147483639, -2176, 2406, 589824, 1227787, -18)
4

3 に答える 3

2

最後に、物事はより明確に見えます。

これは、インテルFortranコンパイラーのユーザーガイドとリファレンスガイドです。「レコードタイプ:可変長レコード」のセクションを参照してください。

レコード長が2,147,483,639バイトを超える場合、レコードはサブレコードに分割されます。サブレコードは、1から2,147,483,639までの任意の長さにすることができます。

先頭の長さフィールドの符号ビットは、レコードが継続されるかどうかを示します。末尾の長さフィールドの符号ビットは、先行するサブレコードの存在を示します。符号ビットの位置は、ファイルのエンディアン形式によって決まります。

継続されるサブレコードには、符号ビット値が1の先行長フィールドがあります。レコードを構成する最後のサブレコードには、符号ビット値0の先行長フィールドがあります。先行サブレコードを持つサブレコードには、末尾長があります。符号ビット値が1のフィールド。レコードを構成する最初のサブレコードには、符号ビット値が0の末尾の長さフィールドがあります。符号ビットの値が1の場合、レコードの長さは2で格納されます。補数表記

多くのエッセイを読んだ後、私は2の補数表記に誤解されていることに気付きました。レコードマーカーは、上記の規則に従って符号を変更するだけで、符号ビットが1の場合は2の補数表記に変更します。データは異なるコンパイラで作成されました。

以下が解決策です。

データは2GBを超えるため、いくつかのサブレコードに分割されます。最初のレコード開始マーカーは-2147483639であるため、最初のレコードの長さは2147483639であり、これはサブレコードの最大長であり、私が思った2147483640や2の補数表記-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

レコードの開始マーカーと終了マーカーは、記号を除いて常に同じであることがわかります。3つのレコードの長さは2147483639、2147483639、18バイトであり、4の倍数である必要はありません。したがって、最初のレコードは特定の整数の最初の3バイトで終わり、2番目のレコードは残りの1バイトで始まります。

于 2013-04-24T13:31:37.077 に答える
0

PythonがFortranデータにアクセスするには、f2pyを使用する方が便利な方法であることがわかりました。ただし、レコードマークの奇妙な動作は疑問のままです。少なくとも、Fortranのフォーマットされていないファイル構造に飛び込む(時には混乱する)ことを避けることができます。そして、それはnumpyとよく一致します。

F2PYユーザーガイドおよびリファレンスマニュアルはこちらです。これは、ファイルを開いたり閉じたり、整数の1次元配列と浮動小数点の2次元配列を読み取るためのFortranソースファイルの例です。コメントは!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 
于 2013-03-25T07:08:08.563 に答える
0

この質問は頻繁に出てくるようですので、これはバイナリファイルをスキャンして、それがFortranのフォーマットされていないシーケンシャルアクセスファイルであるかどうかを判断するためのPythonユーティリティコードです。いくつかのヘッダー形式を試すことで機能します。もちろん、「フォーマットされていない」フォーマットは標準ではないため、他のバリエーションもあり得ますが、これは最も一般的なものに当てはまるはずです。

左角かっこはエスケープされているため、変更が必要になる場合があります< これを画面コピーする場合は、「より小さい」記号に戻ります。


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 

于 2013-03-27T21:08:44.743 に答える