如何从文件中读取字节

3
我正在尝试读取.lrf文件中一些元数据的长度。(与LoLReplay程序一起使用)
这些文件没有真正的文档,但我已经在C++中找到了如何做到这一点。我正在尝试用Python重新编写项目,出于多种原因,但我遇到了一个错误。
首先解释一下,.lrf文件在文件开头就有元数据,格式如下:
- 前4个字节是我不知道的东西。 - 接下来的4个字节以十六进制形式存储元数据的长度,直到元数据结束为止,在此之后是重播的实际内容。 - 初始8个字节之后的字节是json格式的元数据
我遇到的问题实际上是读取元数据长度。这是我目前拥有的函数:
def getMetaLength(self):
    try:
        file = open(self.file,"r")
    except IOError:
        print ("Failed to open file.")
        file.close()
    #We need to skip the first 4 bytes.
    file.read(4)
    mdlength = file.read(4)
    print(hex(mdlength))
    file.close()

当我调用此函数时,命令行会返回一条回溯信息,指出:
    Traceback (most recent call last):
    File "C:\Users\Donald\python\lolcogs\lolcogs_main.py", line 6, in <module>
    lolcogs.getMetaLength()
    File "C:\Users\Donald\python\lolcogs\LoLCogs.py", line 20, in getMetaLength
    file.read(4)
    File "C:\Python32\lib\encodings\cp1252.py", line 23, in decode
    return codecs.charmap_decode(input,self.errors,decoding_table)[0]
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 3648:       character maps to <undefined>

我最好的猜测是read()试图读取以某种Unicode格式编码的字符,但这些明确只是我尝试读取的字节。有没有一种方法可以将它们作为字节读取?此外,在尝试读取文件时,是否有更好的跳过字节的方法?


尝试以二进制模式打开文件:f = open(self.file,"rb")。此外,不要将其命名为 file,因为它会与内置的 file 类型名称冲突。 - Paulo Bu
@PauloBu似乎再也没有这种类型了,不过...... - Oleh Prypin
在Python2.7中有定义,在Python3中没有。但是根据阅读OP的代码,他可能正在使用Python 3,所以请忽略我的评论 :) - Paulo Bu
@PauloBu 谢谢,我使用了"rb"而不是只用"r",现在我得到了一个错误:"TypeError: 'bytes' object cannot be interpreted as an integer",但是C++版本必须做一些棘手的事情,所以我已经有了一个大致的想法来解决这个问题。 - shadefinale
3个回答

3

在Python 3中,默认情况下以系统的编码方式打开文本模式的文件。您需要以二进制模式打开文件:

file = open(self.file, 'rb')

你将遇到的另一个问题是,file.read(4)会给你一个由4个字节组成的字符串(而hex函数无法理解)。你可能想要一个整数。为此,请参考int.from_bytes,或更一般地说,参考struct模块。然后你可以按十六进制格式打印该数字,如下所示:

mdlength = int.from_bytes(file.read(4), byteorder='big')
print(hex(mdlength))

太棒了!int.from_bytes() 函数正是我所需要的。在 C++ 中,我不知道是否有相应的函数,但我必须在 C++ 中手动完成这个操作,并且在阅读了你的评论之前,我准备在 Python 中手动完成它!谢谢! - shadefinale

3

二进制文件应该以二进制模式处理:

f = open(filename, 'rb')

在跳过字节方面,我通常使用文件的seek(SEEK_CUR或SEEK_SET),或者如果我不想麻烦的话,我会随意使用file.read(n)。只有当我想要跳转到特定位置时,我才真正使用寻址。

对于解释二进制数据,我只是坚持使用struct模块提供的解包方法,这使得定义你想将一系列字节解释为int、float、char等变得容易。我多年来一直都是这样做的,也许还有更有效的方法,比如其他答案中描述的from_bytes方法。

使用结构体模块,你可以做到:

struct.unpack("3I", f.read(12))

一次性读取3个(无符号)整数。例如,根据您逆向工程的格式,我可能只需要这样说:

unk, size = struct.unpack("2I", f.read(8))
data = f.read(size)

1
你应该以二进制模式打开文件:open(filename, 'rb')

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