0

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

我知道该文件是火星上海拔的地图,大概是二维的。它有一个简短的 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 个字节的十六进制转储(“od -H -j 14400 -N 30 elevmap.assoc”,如果我误解了请告诉我)如下所示:

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

5 回答 5

4

前 30 个字节的 hexdump(我做了“od -H -N 30 elevmap.assoc”)如下所示:

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

这些显然是以小端序存储的,因此您应该反转每个字节序列。在这里,我已为您将其翻译为 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 个字节的十六进制转储(“od -H -j 14400 -N 30 elevmap.assoc”,如果我误解了请告诉我)如下所示:

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

标题说有 7200 x 3600 INT 值。INT有多大?如果 INT 与int大多数 Unix 系统(4 字节)一样大,那么文件的总大小应该是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), DATA(3,Y), ..., DATA(1,Y+1),DATA(2,Y+1)等。您始终可以使用绘制数据PIL 或任何其他 Python 图像处理包,看看你是否得到类似于地形图或杂乱无章的东西。

如果struct.unpack()按照 mgilson 的建议解压数据,您应该使用有h符号短整数值的格式:

data = struct.unpack('%dh' % (7200*3600), f.read(7200*3600*2))
于 2012-07-10T16:32:56.790 回答
2

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

通常不能在 Fortran 中指定字节顺序。您可以使用十六进制编辑器推断的另一个方面。Gfortran 有一个扩展来指定字节顺序。

于 2012-07-10T02:15:03.530 回答
2

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

为了完整起见,万一其他人偶然发现这篇文章试图读取一个 assoc 文件,这里是对我有用的 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()

它返回了这张漂亮的小地图:火星海拔

这是火星,南方在升,但这很容易解决。再次感谢大家。

于 2012-07-10T18:22:37.890 回答
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 代码),那也会很有帮助。

于 2012-07-10T00:17:21.767 回答
1

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

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

剩下的问题:

  • 字节字节序(即 623 可以保存为 00 00 02 6f(大字节序)或 6f 02 00 00(小字节序)

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

如果您能给我们提供整体文件大小和前 30 多个字节数据的十六进制转储,那真的很有帮助。

于 2012-07-10T00:23:49.357 回答