【问题标题】:python/numpy generated binary file to be read by Cpython/numpy 生成的二进制文件由 C 读取
【发布时间】:2017-08-25 19:35:14
【问题描述】:

我正在 python 中创建一个名为 random_from_python_int.dat 的 5*7 整数矩阵二进制文件,然后我从 C 中读取这个二进制文件。不知何故我无法获得正确的数字 这是我生成这个矩阵的python代码:

import numpy as np
np.random.seed(10)
filename = "random_from_python_int.dat"
fileobj = open(filename, mode='wb')
b = np.random.randint(100, size=(5,7))
b.tofile(fileobj)
fileobj.close

这将生成一个矩阵

[ [  9 15 64 28 89 93 29]
  [  8 73 0  40 36 16 11]
  [ 54 88 62 33 72 78 49]
  [ 51 54 77 69 13 25 13]
  [ 92 86 30 30 89 12 65] ]

但是当我从下面的 C 代码中读取它时:

#include <stdio.h>
#include <math.h>
int main()
{
  /* later changed 'double' to 'int', but that still had issues */
  double randn[5][7];

  char buff[256];
  FILE *latfile;

  sprintf(buff,"%s","random_from_python_int.dat");
  latfile=fopen(buff,"r");
  fread(&(randn[0][0]),sizeof(int),35,latfile);
  fclose(latfile);
  printf("\n %d     %d     %d     %d     %d     %d     %d",randn[0][0],randn[0][1],randn[0][2],randn[0][3],randn[0][4],randn[0][5],randn[0][6]);
  printf("\n %d     %d     %d     %d     %d     %d     %d",randn[1][0],randn[1][1],randn[1][2],randn[1][3],randn[1][4],randn[1][5],randn[1][6]);
  printf("\n %d     %d     %d     %d     %d     %d     %d",randn[2][0],randn[2][1],randn[2][2],randn[2][3],randn[2][4],randn[2][5],randn[2][6]);
  printf("\n %d     %d     %d     %d     %d     %d     %d",randn[3][0],randn[3][1],randn[3][2],randn[3][3],randn[3][4],randn[3][5],randn[3][6]);
  printf("\n %d     %d     %d     %d     %d     %d     %d\n",randn[4][0],randn[4][1],randn[4][2],randn[4][3],randn[4][4],randn[4][5],randn[4][6]);
}

它会给我(调整空间以避免在 stackoverflow 网站上滚动):

      28      15         64      93         29 -163754450   9
      40      73          0      16         11 -163754450   8
      33      88         62      17         91 -163754450  54
     256       0 1830354560       0    4196011 -163754450 119
 4197424 4197493 1826683808 4196128 2084711472 -163754450  12

我不确定出了什么问题。我已经尝试在 python 中编写一个浮点矩阵并在 C 中将其读取为 double,它工作正常。但是这个整数矩阵是行不通的。

【问题讨论】:

  • 您将整数读入双精度数。
  • 所以在整数与双重混淆之后,剩下的问题是:你怎么知道“整数”numpy 写入的大小与“int”C 使用的大小相同?
  • 糟糕!但是在我将 double 更改为 int 之后,我得到了 9 0 15 0 64 0 28 0 89 0 93 0 29 0 8 0 73 0 0 0 40 0 36 0 16 0 11 0 54 0 88 0 62 0 33。
  • "你怎么知道 "integer" numpy 写入的大小与 "int" C 使用的大小相同" - 这是一个非常好的想法。你也许可以谷歌它。在 C 中,您可以通过使用类型 int32_t、int64_t 来强制设置大小
  • 第一步:latfile=fopen(buff,"r"); --> latfile=fopen(buff,"rb");(加b

标签: python c numpy io binary


【解决方案1】:

正如@tdube 所写,您的问题的快速总结是:您的numpy 实现写入64 位整数,而您的C 代码读取32 位整数。

至于更多细节,请继续阅读。

当您将整数作为二进制补码数据写入和读取时,您需要确保二进制数据的生产者和消费者的以下三个整数属性相同:整数 大小 , 整数 endianness, 整数 signedness

numpy 和 C 的 signednesssigned 的,所以我们在这里有一个匹配项。

endianness 在这里不是问题,因为 numpy 和 C 程序都在 同一台机器上,因此您可能有 same endianness em>(不管它实际上是什么字节序)。

但是,尺寸是个问题。

默认情况下,numpy.random.randint 使用np.int 作为其dtypenp.int 在文档中的大小未知,但在您的系统上却是 64 位。

numpy scalars reference 列出了一些整数类型(明显不包括np.int),其中三种组合对于与numpy 之外的程序进行健壮接口很有趣:

 # | numpy    | C
---+----------+---------
 1 | np.int32 | int32_t
 2 | np.int64 | int64_t
 3 | np.intc  | int

如果您只是碰巧将基于numpy 的软件连接到用于构建numpy 的相同C 环境,则使用(np.intc, int) 类型对(来自案例3)看起来很安全.

但是,出于以下原因,我更喜欢显式大小的类型之一(案例12):

  • numpy 和 C 中整数的大小绝对显而易见。

  • 因此,您可以使用 numpy 生成的输出与使用不同 C 环境编译的程序进行接口,该程序可能具有不同的大小 int

  • 您甚至可以使用您的numpy 生成的输出来连接以完全不同的语言编写或编译并在完全不同的机器上运行的程序。不过,您必须考虑不同机器场景的字节顺序。

【讨论】:

  • np.int 不是 numpy 类型。这只是内置 python int 的一个令人困惑的别名,删除为时已晚。
  • 事实是:np.int 没有记录在 numpy 标量参考中,但 numpy.random.randint 记录使用 np.int 作为默认类型。也许修改文档?
  • 这是 randint 文档中的一个错误,我想我最近在 master.xml 中修复了这个错误。 np.int 不应该在 numpy 标量页面上,因为它不是一个 - 但也许应该有一个警告明确说明这一点。 (对于 np.float、np.complex、...)
  • 好收获。似乎我错过了#9517 中的 pyx 文件
【解决方案2】:

简答

您的 Python 程序输出 64 位整数,而不是您尝试使用 C 程序读取的 32 位整数。

您可以更改以下代码行:

b = np.random.randint(100, size=(5,7), dtype=np.int32)

现在您将在输出文件中看到 32 位整数。

如何判断 Python 代码输出的内容

您的 Python 代码基于对输出文件的 hexdump 的以下分析转储 64 位整数。当然,您可以使用任何十六进制编辑器应用程序检查二进制数据文件。

$ hexdump random_from_python_int.dat
0000000 09 00 00 00 00 00 00 00 0f 00 00 00 00 00 00 00
0000010 40 00 00 00 00 00 00 00 1c 00 00 00 00 00 00 00
0000020 59 00 00 00 00 00 00 00 5d 00 00 00 00 00 00 00

正如@ndim 在他的回答中指出的那样,二进制补码整数表示由三个主要元素组成:[storage] sizeendianness签名。我不会重复他在回答中提供的信息,除了展示如何从上述输出中推断出这些信息,这是我在原始答案中开始做的。

对于多维数组,您可能还需要知道线性存储中元素的order

推断整数存储大小

由于您从np.random.randint() 间接指定(十进制)100 的最大非包含随机值,因此您的值将在十进制范围内[0, 100) 或十六进制的[0x0, 0x64),都可以表示为一个“十六进制字节”。请注意,上述hexdump 输出中的非00 十六进制字节均不在此范围之外。如您所见,总共有 8 个字节用于表示每个整数值(1 个非00-byte 和 7 个00-bytes,基于本例中的数字范围)。

推导字节序

此外,您现在还可以推导出整数表示的字节序,在这种情况下,little endianleast significant bit (LSB) 是线性存储中第一个字节的一部分。 LSB 也可以称为最低有效字节

推断签名

在这种情况下,您无法推断出有符号性,因为您的抽样中没有负值。如果您这样做了,在二进制补码表示中,您会看到 有符号位 的值为1。我不会深入研究二进制补码负整数表示的细节,这与这个问题无关。

推导多维数组顺序

检查上述输出中从文件偏移量 (0x) 0000000(以及未标记的 0000008)开始的前两个 8 字节小端整数是十六进制值 0x00000000 00000009 和 @ 987654341@,分别为915的十进制值。十进制值9 将是row-major order列主要顺序 中的第一个值,但线性存储中的第二个十进制值15 表示行- row 元素在连续存储中的主要顺序。

位于文件偏移量 (0x) 0000010 的第三个整数值的十六进制值是 0x00000000 00000040,十进制是数值 64。此值是您的预期输出中的第三个值,行主要顺序

为了完整起见,列优先顺序将输出8 的十进制值作为线性存储中表示的第二个整数。

如何在 Python 代码中生成 Numpy 转储 32 位数字

要使您的代码转储 32 位数字,这是 int 的常见实现长度(但它是 C 标准中的“实现定义”,仅指定 int 表示的最小范围),您可以更改以下代码行:

b = np.random.randint(100, size=(5,7), dtype=np.int32)

现在您将在输出文件中看到 32 位整数。

$ hexdump random_from_python_int.dat
0000000 09 00 00 00 0f 00 00 00 40 00 00 00 1c 00 00 00
0000010 59 00 00 00 5d 00 00 00 1d 00 00 00 08 00 00 00
0000020 49 00 00 00 00 00 00 00 28 00 00 00 24 00 00 00

注意:C int 变量的实际存储大小(精度)是“实现定义的”,这意味着您可能需要在输出之前调整 numpy 数组整数存储大小以最大程度地与 C 兼容。请参阅 @ndim 的很好的答案,提供了有关此的更多详细信息。

对 C 代码的更改

您的 C 代码必须更新以反映二维数组数据类型的变化。在您的代码中,double randn[5][7] 应该是 int randn[5][7]。正如@ndim 指出的那样,您也可以创建int32_t 类型,但是您的编译器可能会发出错误并建议数据类型__int32_t(这是我系统上inttypedef)。进行更改并编译后,我得到以下输出:

 9     15     64     28     89     93     29
 8     73     0     40     36     16     11
 54     88     62     33     72     78     49
 51     54     77     69     13     25     13
 92     86     30     30     89     12     65

更新(另见更新#2)

根据下面@ndim 的评论,您也可以使用np.intc,如下所示。 此选项可能是最佳选项,除非您针对整数表示的特定存储大小。

b = np.random.randint(100, size=(5,7), dtype=np.intc)

我对此进行了测试,它也可以生成 32 位整数。

更新 #2

我完全同意@ndim 的观点,即指定整数大小最适合最大化兼容性。 “最少惊喜”的 Python 习语在这里适用。

【讨论】:

  • 根据 numpy 文档,np.intc 将使用并写入 numpy 中的文件,无论 C 认为是 int
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-01
  • 2011-09-03
  • 2016-01-15
  • 1970-01-01
  • 2017-02-07
  • 2021-03-29
相关资源
最近更新 更多