如何使用 QDataStream 在 Python 中打开 bin 文件

Posted

技术标签:

【中文标题】如何使用 QDataStream 在 Python 中打开 bin 文件【英文标题】:How to open a bin file in Python using QDataStream 【发布时间】:2016-11-22 14:41:50 【问题描述】:

我有一个在应用程序中编码的 bin 文件,我需要访问该文件并将其转换为 csv 文件。我已经获得了文档,但不确定如何在 Python 中访问该文件的内容。

以下是有关数据集如何序列化的一些详细信息

Datasets.bin 是使用 Qt 的 QDataStream 序列化使用版本 QDataStream::Qt_4_7 序列化的 DataSet 类的列表。

The format of the datasets.bin file is:

quint32 Magic Number    0x46474247
quint32 Version     1
quint32 DataSet Marker  0x44415441
qint32      # of DataSets       n
DataSet DataSet 1
DataSet DataSet 2
     .
     .
     .
     .
DataSet DataSet n


The format of each DataSet is:

quint32     Magic Number    0x53455455  
QString     Name
quint32     Flags           Bit field (Set Table)
QString     Id          [Optional]  
QColor      Color           [Optional]
qint32          Units           [Optional]
QStringList         Creator Ids     [Optional]
bool            Hidden          [Optional]
QList<double>   Thresholds      [Optional]
QString         Source          [Optional]
qint32          Role            [Optional]
QVector<QPointF>    data points

我一直在查看 PyQt4 数据流文档,但似乎找不到任何具体示例。任何为我指明正确方向的帮助都会很棒

【问题讨论】:

使用 QDataStream 是否最简单,例如使用 Qt Python 绑定? dl.dropboxusercontent.com/u/28824868/datasets.bin 如果有人想测试它,这里是数据集文件的链接 @MichaelBawol。我尝试使用 C++ 读取该文件,但在第一个 Source 条目处失败。因此,格式不完整/错误或文件已损坏。你从哪里得到文件?你有没有一组已知值的小玩具示例? 每个数据集 bin 都存储有一个 xml 文件。 dl.dropboxusercontent.com/u/28824868/session.xml 数据集来自我为下载和操作传感器数据而构建的自定义软件。公司已经倒闭并为我提供了格式。这是我收到的完整格式文档。 dl.dropboxusercontent.com/u/28824868/… @MichaelBawol。我能够创建一个 C++ 工具,该工具可以读取格式化文档中包含的数据集文件。但是,它无法读取其他数据集文件,我现在可以确定该文件已损坏或格式不同。我已经根据我学到的知识更新了我的答案,但我仍然看不到如何在 PyQt 中正确读取 QList/QVector 类型。我怀疑这可能是不可能的。但是,我的答案中的当前代码至少显示了 如何 正确处理格式。 【参考方案1】:

PyQt 无法像在 C++ 中那样读取所有数据,因为它无法处理模板类(如 QList&lt;double&gt;QVector&lt;QPointF&gt;),这需要 Python 中不可用的特定语言支持。这意味着必须使用变通方法。幸运的是,datastream format 非常简单,因此读取任意模板类可以简化为一个简单的算法:将长度读取为 uint32,然后迭代 range 并将包含的元素一个接一个地读取到list:

points = []
length = stream.readUInt32()
for index in range(length):
    point = QPoint()
    stream >> point
    points.append(point)

下面是一个脚本,展示了如何正确读取整个数据集格式:

from PyQt4 import QtCore, QtGui

FLAG_HASSOURCE = 0x0001
FLAG_HASROLE = 0x0002
FLAG_HASCOLOR = 0x0004
FLAG_HASID = 0x0008
FLAG_COMPRESS = 0x0010
FLAG_HASTHRESHOLDS = 0x0020
FLAG_HASUNITS = 0x0040
FLAG_HASCREATORIDS = 0x0080
FLAG_HASHIDDEN = 0x0100
FLAG_HASMETADATA = 0x0200

MAGIC_NUMBER = 0x46474247
FILE_VERSION = 1
DATASET_MARKER = 0x44415441
DATASET_MAGIC = 0x53455455

def read_data(path):
    infile = QtCore.QFile(path)
    if not infile.open(QtCore.QIODevice.ReadOnly):
        raise IOError(infile.errorString())

    stream = QtCore.QDataStream(infile)
    magic = stream.readUInt32()
    if magic != MAGIC_NUMBER:
        raise IOError('invalid magic number')
    version = stream.readUInt32()
    if version != FILE_VERSION:
        raise IOError('invalid file version')
    marker = stream.readUInt32()
    if marker != DATASET_MARKER:
        raise IOError('invalid dataset marker')
    count = stream.readInt32()
    if count < 1:
        raise IOError('invalid dataset count')

    stream.setVersion(QtCore.QDataStream.Qt_4_7)

    rows = []
    while not stream.atEnd():
        row = []

        magic = stream.readUInt32()
        if magic != DATASET_MAGIC:
            raise IOError('invalid dataset magic number')

        row.append(('Name', stream.readQString()))

        flags = stream.readUInt32()
        row.append(('Flags', flags))

        if flags & FLAG_HASID:
            row.append(('ID', stream.readQString()))
        if flags & FLAG_HASCOLOR:
            color = QtGui.QColor()
            stream >> color
            row.append(('Color', color))
        if flags & FLAG_HASUNITS:
            row.append(('Units', stream.readInt32()))
        if flags & FLAG_HASCREATORIDS:
            row.append(('Creators', stream.readQStringList()))
        if flags & FLAG_HASHIDDEN:
            row.append(('Hidden', stream.readBool()))
        if flags & FLAG_HASTHRESHOLDS:
            thresholds = []
            length = stream.readUInt32()
            for index in range(length):
                thresholds.append(stream.readDouble())
            row.append(('Thresholds', thresholds))
        if flags & FLAG_HASSOURCE:
            row.append(('Source', stream.readQString()))
        if flags & FLAG_HASROLE:
            row.append(('Role', stream.readInt32()))

        points = []
        length = stream.readUInt32()
        for index in range(length):
            point = QtCore.QPointF()
            stream >> point
            points.append(point)
        row.append(('Points', points))
        rows.append(row)

    infile.close()

    return rows

rows = read_data('datasets.bin')

for index, row in enumerate(rows):
    print('Row %s:' % index)
    for key, data in row:
        if isinstance(data, list) and len(data):
            print('  %s = [%s ... ] (%s items)' % (
                  key, repr(data[:3])[1:-1], len(data)))
        else:
            print('  %s = %s' % (key, data))

【讨论】:

“我不知道它是否能够读取 QList 或 QVector,因为 PyQt 不能直接支持 C++ 模板类。” - 我认为这本身就是一个很好的问题。 这有点奏效。它访问内容,并将一些信息附加到行列表中。尽管这些信息很少,但我可以看到如果没有文件本身会很困难。我已经把它包括在上面了。我仍在深入研究此代码以更好地理解它。

以上是关于如何使用 QDataStream 在 Python 中打开 bin 文件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 QDataStream::readBytes()

如何从 QDataStream 中读取数组

如何使用 QTextStream 而不是 QDataStream 从 QTableView 进行加载保存?

PyQt:使用 QDataStream 保存本机 QTreeWidget

如何使用结构化 QDataStream 和序列化?

在 C# 中使用 QDataStream 读取在 QT 中创建的二进制文件