我有一个.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
答案 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
看起来非常像0e97
和0e93
在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 format或Reading 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()
这返回了这张漂亮的小地图:
这是火星,南方正在上升,但这很容易解决。再次感谢所有人。
答案 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个字节数据的十六进制转储,那真的会有所帮助。