【问题标题】:Parse stream in x length chunks based on uint32 id基于 uint32 id 以 x 长度的块解析流
【发布时间】:2021-10-06 10:35:18
【问题描述】:

我几乎没有 C/Qt 经验,并且有一个需要移植到 Python 的小型解析器。有谁能够解释我如何在 Python 中实现以下内容?我明白结果是什么,只是不明白如何实现 uint32 实例化和移位,从而导致基于 4 字节“id”的不同部分长度。我希望只使用本机 Python 3.5+ 包来解析它,如果它使打字方便,numpy 或类似的就可以了。

QDataStream stream(item);
stream.setByteOrder(QDataStream::LittleEndian);
Items parts;
while (!stream.atEnd()) {
    quint32 partId;
    stream >> id;
    char *bytes;
    uint length;
    stream.readBytes(bytes, length);
    parts.append(QPair<quint32, QByteArray>(id, QByteArray(bytes, length)));
    delete bytes;
}
return parts;

【问题讨论】:

    标签: python python-3.x pyqt pyqt5 qdatastream


    【解决方案1】:

    由于python中的数值类型与C++的数值类型不匹配,所以QDataStream不再使用“>>”运算符来获取值,而是具有特定的方法,例如readUInt32。

    考虑以下生成数据的代码:

    #include <QDataStream>
    #include <QFile>
    #include <QDebug>
    
    int main()
    {
        QFile file("file.dat");
        if(!file.open(QIODevice::WriteOnly)){
            qDebug() << file.error() << file.errorString();
            return EXIT_FAILURE;
        }
        QDataStream stream(&file);
        stream.setByteOrder(QDataStream::LittleEndian);
        stream.setVersion(QDataStream::Qt_5_15);
    
        QVector <QPair<quint32, QByteArray>> data;
        data.append({1, "One"});
        data.append({2, "Two"});
        data.append({3, "Three"});
    
        for(const QPair<quint32, QByteArray> & d: qAsConst(data)){
            stream << d.first;
            stream.writeBytes(d.second.constData(), d.second.size());
        }
        return EXIT_SUCCESS;
    }
    

    以下代码获取数据:

    import sys
    
    from PyQt5.QtCore import QByteArray, QDataStream, QFile, QIODevice
    
    
    file = QFile("file.dat")
    if not file.open(QIODevice.ReadOnly):
        print(file.error(), file.errorString())
        sys.exit(-1)
    
    items = []
    
    stream = QDataStream(file)
    stream.setByteOrder(QDataStream.LittleEndian)
    stream.setVersion(QDataStream.Qt_5_15)
    while not stream.atEnd():
        id_ = stream.readUInt32()
        data = stream.readBytes()
        items.append((id_, QByteArray(data)))
    print(items)
    

    输出:

    [(1, PyQt5.QtCore.QByteArray(b'One')), (2, PyQt5.QtCore.QByteArray(b'Two')), (3, PyQt5.QtCore.QByteArray(b'Three'))]
    

    如果使用 PySide2,那么实现会发生一些变化。

    import sys
    
    from PySide2.QtCore import QByteArray, QDataStream, QFile, QIODevice
    
    
    file = QFile("file.dat")
    if not file.open(QIODevice.ReadOnly):
        print(file.error(), file.errorString())
        sys.exit(-1)
    
    items = []
    
    stream = QDataStream(file)
    stream.setByteOrder(QDataStream.LittleEndian)
    stream.setVersion(QDataStream.Qt_5_15)
    while not stream.atEnd():
        id_ = stream.readUInt32()
        data = QByteArray()
        stream >> data
        items.append((id_, data))
    print(items)
    

    输出:

    [(1, PySide2.QtCore.QByteArray(b'One')), (2, PySide2.QtCore.QByteArray(b'Two')), (3, PySide2.QtCore.QByteArray(b'Three'))]
    

    更新:

    如果不使用 QDataStream 是无法获取数据的,因为 Qt 对每种类型的数据都使用自己的格式,而且这种格式不是一个标准,每个版本都可以在不通知的情况下更改。为此,必须指明所使用的QDataStream的字节序和版本。

    更新 2

    假设 QDataStream 用于打包 quint32 和字节的格式是 QDataStream.Qt_5_15 那么可能的实现是:

    import sys
    import struct
    
    items = []
    
    with open("file.dat", "rb") as f:
        while True:
            try:
                (id_,) = struct.unpack("I", f.read(4))
                (length,) = struct.unpack("I", f.read(4))
                data = f.read(length)
            except (EOFError, struct.error) as e:
                break
            else:
                items.append((id_, data))
    
    print(items)
    

    输出:

    [(1, b'One'), (2, b'Two'), (3, b'Three')]
    

    【讨论】:

    • 非常感谢,您的实现非常清晰,我理解这个上下文,因为 PyQT5.Core 实现了 Qt 类型,我应该更具体一点,我想使用原生 python尽可能少的依赖(虽然理解像 numpy 这样的东西可能会使打字更方便。我已经更新了我的原始帖子以反映。
    • @circa 简短回答:你不能。长答案:查看我的更新答案
    • 嘿,谢谢,这让我吃惊,数据实际上并不是由 Qt 生成的,不知道使用什么进程或语言来创建二进制文件,恰好这个解析器最初是作为一个Qt 中的原型,还有 C#,我需要将其移至 Python。我得到 uint32 只是一个 4 字节无符号整数,我可以用 struct.unpack("I", stream.read(4)) 读取它,但这给了我 4 字节块,而不是原始整数的“神奇”长度......
    • @circa 如果您需要更多帮助必须提供minimal reproducible example,即我的意思是对格式进行清晰准确的描述(例如,每个数据有多少字节,在这种情况下) uint32 很简单,但是字节是如何编码的?数据是在数据之前还是之后指示的?)或者至少是输入数据(原始数据)以及你应该得到什么或者生成数据的任何语言的代码。我指出这一点是因为我假设数据是使用 QDataStream 生成的。
    • @circa 展示在 C# 中读取数据的代码也很有趣,它可能不使用外部库,而是使用自己的工具实现。
    猜你喜欢
    • 1970-01-01
    • 2016-12-31
    • 1970-01-01
    • 2013-09-24
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多