关联(.assoc)二进制文件结构/探测未知文件结构

时间:2012-07-10 00:00:49

标签: python fortran binaryfiles

我有一个.assoc结尾的文件,显然是一个'二进制关联文件',虽然我在网上找不到那种文件的信息。它由fortran和idl读取,是49Mb,我正在尝试将其读入python。这可能是一个悬而未决的问题,但任何人都可以建议一种方法来探测文件的结构,以便了解我如何阅读它?

我知道这个文件是火星上海拔的地图,大概是2D。它有一个简短的ascii标题:

        7200         3600 MOLA .05 dg/px topo 5/2002

---------------------------------------------------------

header length        14400 bytes

map X size                7200

map Y size                3600

no-data value            30303

maximum value            21197

minimum value            -8204

The map is stored as an INT array with X as

longitude and Y as latitude. The map is assumed to be

global in coverage.

--------------------------------------------------------- 

对于格式不正确的问题感到抱歉,但对于如何探测未知文件类型的一般建议将非常感激。或者如果您知道这种文件类型,那就更好了!

以下是读取文件的idl代码片段:

ELMAP='elevmap.assoc'
OPENR, ELUN, ELMAP, /GET_LUN
B = ASSOC(ELUN,BYTARR(100))              ; assoc header
HEADER = STRING(B[0])                    ; read the header
NLON = 0                                 ; 'fix' no. of longitudes
NLAT = 0                                 ; 'fix' no. of latitudes
READS,HEADER,NLON,NLAT                   ;  read no. of lons/lats
EXG = NLON/360                           ; longitude scale (pix/deg)
EYG = NLAT/180                           ; latitude scale (pix/deg)
EMAP = ASSOC(ELUN,INTARR(1),14400)

前30个字节的hexdump(我做了“od -H -N 30 elevmap.assoc”)看起来像这样:

0000000          20202020        20202020        30303237        20202020
0000020          20202020        30363320        4f4d2030        0000414c
0000036

标题后的前30个字节的hexdump(“od -H -j 14400 -N 30 elevmap.assoc”,请告诉我,如果我误解了这个),看起来像这样:

0034100          0e970e93        0ea50e9d        0ea50ea5        0ea50ea5
0034120          0ea50ea5        0ea40ea4        0ea20ea3        00000ea2
0034136

5 个答案:

答案 0 :(得分:4)

  

前30个字节的hexdump(我做了“od -H -N 30 elevmap.assoc”)看起来像这样:

0000000          20202020        20202020        30303237        20202020
0000020          20202020        30363320        4f4d2030        0000414c
0000036

这些显然存储在little endian中,因此你应该反转每个字节序列。在这里,我已经为您翻译成ASCII:

0000000          20 20 20 20 20 20 20 20 37 32 30 30 20 20 20 20
                  _  _  _  _  _  _  _  _  7  2  0  0  _  _  _  _
0000020          20 20 20 20 20 33 36 30 30 20 4d 4f 4c 41
                  _  _  _  _  _  3  6  0  0  _  M  O  L  A
  

标题后的前30个字节的hexdump(“od -H -j 14400 -N 30 elevmap.assoc”,请告诉我,如果我误解了这个),看起来像这样:

0034100          0e970e93        0ea50e9d        0ea50ea5        0ea50ea5
0034120          0ea50ea5        0ea40ea4        0ea20ea3        00000ea2

Header表示有7200 x 3600个INT值。 INT有多大?如果在大多数Unix系统(4个字节)上INT与int一样大,则文件的总大小应为7200 x 3600 x 4或接近99 MiB。你说它是49 MiB因此要么使用压缩(不太可能),要么INT是16位(更有可能)。后者的有力证据是你从第二个od得到的结果 - 0e970e93看起来非常像0e970e93在32位整数中被错误地连接起来。人们可以从地形图中看出,邻近的值不应该突然改变(除了在一些深的垂直沟槽或陡峭的山壁的边界)。这也与值在short int: [-32768, 32767]范围内的事实一致。

有了这些知识,上面的转储应该被理解为:

0034100          0e93  0e97  0e9d  0ea5  0ea5  0ea5  0ea5  0ea5
                +3731 +3735 +3741 +3749 +3749 +3749 +3749 +3749
0034120          0ea5  0ea5  0ea4  0ea4  0ea3  0ea2  0ea2
                +3749 +3749 +3748 +3748 +3747 +3746 +3746

现在您只需要弄清楚是否使用了X-major或Y-major数据存储。根据我的经验,大多数数据处理工具遵循Fortran列主要顺序,如果数据为DATA(X,Y),则X应为主要维度,即连续数据值应为DATA(1,Y)DATA(2,Y),{ {1}},...,DATA(3,Y)DATA(1,Y+1)等。您可以随时使用PIL或任何其他Python图像处理程序包绘制数据,看看是否有类似于地形图或什么都搞乱了。

如果按照mgilson的建议使用DATA(2,Y+1)解包数据,则应使用struct.unpack()格式表示签名的短整数值:

h

答案 1 :(得分:2)

以下是一些讨论Fortran如何编写未格式化文件的问题:Fortran unformatted file formatReading a direct access fortran unformatted file in Python。要知道额外字节的位置,您必须知道Fortran中使用了哪些read语句,以便您知道记录结构。可以使用Stream IO在最近的Fortran中绕过额外的字节。使用十六进制编辑器,可以对这些方面进行逆向工

通常无法在Fortran中指定Endianess。您可以使用十六进制编辑器推断出的另一个方面。 Gfortran有一个扩展来指定endianess。

答案 2 :(得分:2)

谢谢大家的帮助。特别是Hristo Iliev对hexdump输出和mgilson的一个很好的解释是一个有用的代码模板。

为了完整性,关于其他人偶然发现这篇文章试图读取一个关联文件的可能性,这里是对我有用的python代码。

import struct
import numpy as np
import matplotlib.pylab as pl

with open('elevmap.assoc','rb') as f:
    f.read(14400)
    data=struct.unpack('%dh' % (7200*3600), f.read(7200*3600*2))

# Now turn it into a numpy array
data = np.array(data).reshape(3600,7200)

pl.figure()
pl.imshow(data)
pl.show()

这返回了这张漂亮的小地图:Mars Elevation

这是火星,南方正在上升,但这很容易解决。再次感谢所有人。

答案 3 :(得分:1)

这不是我见过的任何文件格式,但是,根据标题中的信息,您可以执行以下操作:

import numpy as np
import struct

with open('datafile.assoc','rb') as f:
    nx,ny=f.read(14400).split()[:2]  #here I split the header and only take the array indices.  You could get more fancy with your parsing if you wanted.
    data=struct.upack('%dh'%(nx*ny),f.read(nx*ny*2))

#now turn it into a numpy array:
data = np.array(data).reshape(ny,nx)  #assume "x" is the fast index
data[data==30303] = np.nan
#some checks
print (np.nanmax(data)) # 21197
print (np.nanmin(data)) # -8204

如果这不起作用并且你有生成文件的fortran代码(或者可以读取文件的fortran代码),那么这也会有所帮助。

答案 4 :(得分:1)

根据标题,数据保存为7200 x 3600 INT数组。

最小值为负,因此可能是16位或32位signed int。 (剩余文件的大小)/(7200 * 3600)应该是一个int的大小,以字节为单位。

剩下的问题:

  • byte endian-ness(即623可以保存为00 00 02 6f(big-endian)或6f 02 00 00(little-endian)

  • 逐行或逐列 - 尝试两者并查看哪个看起来正确

如果你能给我们整个文件大小和前30个字节数据的十六进制转储,那真的会有所帮助。