【问题标题】:Parsing a binary file written in MATLAB from Python and vice versa从 Python 解析用 MATLAB 编写的二进制文件,反之亦然
【发布时间】:2016-08-21 09:03:06
【问题描述】:

我在 python 中遇到了struct.unpack 的重大问题。我有一个预先确定格式的二进制文件,可以用 MATLAB 或 Python 编写。

我可以在 Python 中将二进制数据写入文件并毫无问题地读回数据。我还可以将相同的数据从 MATLAB 写入二进制文件,然后在 MATLAB 中毫无问题地读回。

当我从 MATLAB 写入数据并尝试在 Python 中读回数据时,或者当我在 Python 中写入数据并尝试在 MATLAB 中读回数据时,我的问题就出现了。

为简单起见,假设我将两个整数写入二进制文件(大端)。每个整数为 4 个字节。第一个整数是不大于 4 个字节的有效整数,第二个整数必须等于 1、2 或 3。

首先,这是我在 MATLAB 中编写数据的方式:

fid=fopen('hello_matlab.test','wb');
first_data=4+4;
second_data=1;

fwrite(fid,first_data,'int');
fwrite(fid,second_data,'int');

fclose(fid);

这是我在 MATLAB 中读回的内容:

fid=fopen('hello_matlab.test','rb');
first_data=fread(fid,1,'int');
second_data=fread(fid,1,'int');

fprintf('first data: %d\n', first_data);
fprintf('second data: %d\n', second_data);

fclose(fid);

    >> first data: 8
    >> second data: 1

现在,这是我在 Python 中编写数据的方式:

fid=open('hello_python.test','wb')
first_data=4+4
second_data=1

fid.write(struct.pack('>i',first_data))
fid.write(struct.pack('>i',second_data))

fid.close()

这就是我在 python 中读回这些数据的方式。另请注意,注释掉的部分有效(从用 Python 编写的文件中读取时)。我最初认为struct.calcsize('>i') 的计算方式发生了一些奇怪的事情,所以我删除了它,而是放入了一个硬编码常量INTEGER_SIZE,来表示我知道 MATLAB 在编码时使用的字节数:

INTEGER_SIZE=4

fid=open('hello_python.test','rb')

### FIRST WAY I ORIGINALLY READ THE DATA ###
# This works, but I figured I would try hard coding the size
# so the uncommented version is what I am currently using.
#
# first_data=struct.unpack('>i',fid.read(struct.calcsize('>i')))[0]
# second_data=struct.unpack('>i',fid.read(struct.calcsize('>i')))[0]

### HOW I READ DATA CURRENTLY ###
first_data=struct.unpack('>i',fid.read(INTEGER_SIZE))[0]
second_data=struct.unpack('>i',fid.read(INTEGER_SIZE))[0]

print "first data: '%d'" % first_data
print "second data: '%d'" % second_data

fid.close()

    >> first data: 8
    >> second data: 1

现在,假设我想在 MATLAB 中阅读 hello_python.test。使用我当前的 MATLAB 代码,这是新的输出:

>> first data: 419430400
>> second data: 16777216

这很奇怪,所以我做了相反的事情。我查看了阅读hello_matlab.test 时会发生什么。使用我当前的 Python 代码,这是新的输出:

>> first data: 419430400
>> second data: 16777216

所以,发生了一些奇怪的事情,但我不知道它是什么。另请注意,虽然这是一个更大项目的一部分,但我只是将我的代码的这些部分提取到一个新项目中,并用这些结果测试了上面的示例。我真的很困惑如何使这个文件可移植:(任何帮助将不胜感激。

【问题讨论】:

  • 我在 MATLAB 代码中看不到任何表明您正在以大端格式编写值的内容;我怀疑它们是用 little-endian 格式编写的,所以在使用 Python 代码阅读时,你需要<i 而不是<I
  • 另外,在编写了整个 MATLAB 文件之后,用 Python 打开该文件并执行简单的fid.read() 读取全部内容的结果是什么?

标签: python matlab file struct binary


【解决方案1】:

问题在于endianness,即数字中的位顺序。您必须在 x86 或 x86-64 计算机上(因为这些是 MATLAB 唯一支持的),它们是 little-endian。但是,python >i 告诉它使用大端字节序。所以你使用了相反的字节顺序,这使得两种语言读出来的数字完全不同。

如果您只打算在 x86 或 x86-64 计算机上使用 Python 代码,或者您只关心在同一台计算机上的 MATLAB 和 Python 之间发送数据,那么您可以完全去掉字节顺序标记并且使用本机字节顺序(所以i 而不是>i)。如果您可能在 powerpc 系统上运行 python,您可能需要手动指定 little-endianess (<i)。

对于这个例子,这似乎是唯一的问题。我想指出,如果您尝试一次读取和写入数据数组/矩阵,那么numpy.fromfile 会更快更容易。

【讨论】:

  • 如果使用 numpy 就不要使用 np.loadtxt。从速度来看,它将是 np.fromfile>np.load>pd.read_csv>>np.loadtxt (基于softwarerecs.stackexchange.com/questions/7463/…)。
  • 这个问题似乎与字节序有关。我最初认为可以,因为 MATLAB 声称指定 fwrite(...,'int') 意味着 int = 4 bytes。但是,在添加了ieee-be 格式化程序之后,它似乎确实解决了问题。
  • @Alex:你为什么要大端?由于现在您要使用的几乎所有计算机都是小端或双端,因此您只会通过将数据设置为大端来增加不必要的开销。
  • 我的二进制格式要替换的原始文件是以经典格式编写的 netcdf,它使用big endian。因为我认为我最终必须翻译这些文件,所以我想我会让自己更容易并以大端方式读/写。
【解决方案2】:

您可能对 pandas hdf5 商店感兴趣:

在 Python 中:

In [418]: df_for_r = pd.DataFrame({"first": np.random.rand(100),
   .....:                          "second": np.random.rand(100),
   .....:                          "class": np.random.randint(0, 2, (100,))},
   .....:                          index=range(100))
   .....: 

In [419]: df_for_r.head()
Out[419]: 
   class     first    second
0      0  0.417022  0.326645
1      0  0.720324  0.527058
2      1  0.000114  0.885942
3      1  0.302333  0.357270
4      1  0.146756  0.908535

In [420]: store_export = HDFStore('export.h5')

In [421]: store_export.append('df_for_r', df_for_r)

In [422]: store_export
Out[422]: 
<class 'pandas.io.pytables.HDFStore'>
File path: export.h5
/df_for_r            frame_table  (typ->appendable,nrows->100,ncols->3,indexers->[index])

在matlab中:

data = h5read('export.h5','/df_for_r');

但我不确定它是否有效,完全在浏览器中编写...

【讨论】:

  • 我们最初使用 netcdf 并希望转换为我们自己的二进制文件格式,以便它相对独立于语言:( 不幸的是我必须遵守那套准则。我的错,我应该把在原始描述中。
  • 用c++制作怎么样?就像将要写入的数据放在 c 中,然后保存,用 c 读取,放在其他地方?我认为 C 是通用的...我不是在玩那个主题,但我会从 c 开始:) 最简单的方法是使用普通的 csv 文件...最有效的存储上限是使用带有压缩的 pandas hdf5 存储...我知道速度最有效 xD...
  • 是的.. 我想我可以省去麻烦,开始用 C 语言编写它。谢谢你所做的一切!
  • HDF5 非常独立于语言。大多数主要的数字语言都支持它,并且无论如何您都将编写 C 代码,然后您可以使用您选择的语言链接到 HDF5 C、C++ 或 Fortran 库。
猜你喜欢
  • 2011-02-02
  • 2016-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-15
  • 2021-06-23
相关资源
最近更新 更多