用Python从二进制文件中读取整数

103

我正在尝试使用Python读取一个BMP文件。我知道前两个字节表示BMP格式,接下来的4个字节是文件大小。当我执行以下命令时:

fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size = int(fin.read(4))  

我遇到的问题是:

ValueError: 使用10进制将字符串'F#\x13'转换成整数时出现无效字面值

我想要读取这四个字节并将它们作为整数处理,但似乎 Python 将其读取为字符并返回一个字符串,该字符串不能被转换为整数。我应该如何正确地处理这个问题?


2
如果您的目标是使用位图,而不是花时间编写自己的BMP库(虽然这听起来很有趣...),那么您可以使用PIL http://www.pythonware.com/products/pil/,您可能已经安装了它。尝试:导入Image - Jared Updike
9
谢谢Jared,不过我只是想手动阅读bmp文件,只是为了好玩而已! :) - Manuel Araoz
7个回答

142
< p > read方法返回一个字节序列作为字符串。要将字符序列转换为二进制数据,请使用内置的struct模块:http://docs.python.org/library/struct.html

import struct

print(struct.unpack('i', fin.read(4)))
请注意,unpack总是返回一个元组,因此struct.unpack('i', fin.read(4))[0]将给出你想要的整数值。
你可能应该使用格式字符串'<i'(<是一个修饰符,表示小端字节顺序和标准大小和对齐方式-默认情况下使用平台的字节顺序、大小和对齐方式)。根据BMP格式规范,字节应以Intel/小端字节顺序写入。

23
我通常会写成 i, = struct.unpack(...),而不是 i = struct.unpack(...)[0] - Otto Allmendinger
4
我觉得很惊讶的是,在Python中没有内置的函数可以从文件中读取整数(或Short等)类型。我不是Java专家,但我相信它具有原生函数(如readUnsignedShort())来实现这一点。 - Caltor
@Caltor struct.unpack 是一个内置函数(尝试使用 type(struct.unpack))。 - gerrit
@gerrit 你仍需要使用两个函数而不是一个。为什么每件事都必须作为字符串从文件中读取,然后转换为数字,而不是直接从文件中读取数字? - Caltor
@Caltor 至少在Python3中不会被读作字符串,因为struct.unpack期望的是一个bytes对象。从缓冲区或流中读取的优点是我可以传递来自其他流的字节,例如,我可以使用gzip.GzipFile来读取数据,而无需将整个文件解压缩到内存中。 - gerrit
显示剩余3条评论

67

一种不使用 'struct.unpack()' 的替代方法是使用NumPy

import numpy as np

f = open("file.bin", "r")
a = np.fromfile(f, dtype=np.uint32)

'dtype'代表数据类型,可以是int#、uint#、float#、complex#或用户定义的类型。请参阅numpy.fromfile

个人更喜欢使用NumPy处理数组/矩阵数据,因为它比使用Python列表快得多。


16
文件打开可以跳过:a = np.fromfile('file.bin', dtype=np.uint32) - Mathieu Schopfer
在我的情况下,这并没有直接起作用。根据您的编码,您可以尝试更多奇特的数据类型,例如: np.fromfile( file, dtype='>i2') , > 或 < 确定大端或小端。根据字节数,您可以选择 i2 或 i4。 - Adrien Mau
1
对我来说,使用如此庞大和复杂的NumPy包来执行低级和基本操作是一种过度杀伤力的想法。 - Nikolaj Š.

37

从 Python 3.2 开始,你也可以使用 from_bytes 原生的整数方法来实现此操作:

file_size = int.from_bytes(fin.read(2), byteorder='big')

请注意,该函数要求您指定数字是以大端序还是小端序编码的,因此您必须确定字节序以确保其正常工作。


在Python3中,int是动态大小的(与Python2的long相同实现;这有时也被称为“大整数”),我认为这就是首次添加此int方法的动机。https://docs.python.org/3/library/stdtypes.html#:~:text=Integers%20have%20unlimited%20precision. - CrepeGoat
那么为什么是2呢?你怎么知道的? - Gilad
1
哦,这里的数字2只是来自于OP,它并不是什么神奇的数字。通过用某个正整数变量n替换2,就可以将其泛化,而且同样适用。 - CrepeGoat
我想知道为什么你使用大端字节顺序,因为对于大多数人来说正确的选项应该是“小端”。更不用说BMP规范要求使用小端序了。 - Nikolaj Š.
@Klas 谢谢你澄清 BMP 使用小端序,我不知道!我也不确定“大多数人”使用什么,所以说实话我只是随意选择了一个。但重点是你可以根据需要选择 'little''big' 之间的任何一个。 - CrepeGoat
1
大多数现代架构和操作系统都是小端序的,据我所知(尽管了解不够深入)。因此,当我尝试您的方法时,结果很奇怪。这可能会让那些没有准备好进行调查的人感到困惑,并且会损害您的声誉。=) - Nikolaj Š.

6
除了使用`struct`之外,您还可以使用`array`模块
import array
values = array.array('l') # array of long integers
values.read(fin, 1) # read 1 integer
file_size  = values[0]

好的观点。但是这个解决方案不如struct模块的解决方案灵活,因为通过values.read()读取的所有元素都必须是长整数(使用数组模块读取长整数、字节和长整数不太方便)。 - Eric O. Lebigot
我同意。array 是读取二进制文件的一种高效方式,但在处理结构时并不是非常灵活,就像你所正确提到的那样。 - Nick Dandoulakis
1
array.read已被弃用,建议使用array.fromfile自1.51版本开始。 - user334911

4

当您读取二进制文件时,需要将其解包成整数,因此请使用struct模块进行解包。

import struct
fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size, = struct.unpack("i",fin.read(4))

struct.unpack 返回一个元组。 - luc

1
当你从二进制文件中读取数据时,会使用一种叫做bytes的数据类型。它有点像列表或元组,但只能存储0到255之间的整数。
尝试:
file_size = fin.read(4)
file_size0 = file_size[0]
file_size1 = file_size[1]
file_size2 = file_size[2]
file_size3 = file_size[3]

或者:

file_size = list(fin.read(4))

改用:

file_size = int(fin.read(4))

-1

这里是一个晚期的解决方案,但我认为它可能会有所帮助。

fin = open("hi.bmp", "rb")
firm = fin.read(2)
file_size = 0
for _ in range(4):  
    (file_size << 8) += ord(fin.read(1))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接