基于 uint32 id 解析 x 长度块中的流
Parse stream in x length chunks based on uint32 id
我没有什么 C/Qt 经验,并且有一个小的解析器需要移植到 Python。有谁能解释我如何在 Python 中实现以下内容?我明白结果是什么,只是不明白如何根据 4 字节“id”实现导致不同部分长度的 uint32 实例化和移位。我希望只使用本机 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中数值类型与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和bytes的格式是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')]
我没有什么 C/Qt 经验,并且有一个小的解析器需要移植到 Python。有谁能解释我如何在 Python 中实现以下内容?我明白结果是什么,只是不明白如何根据 4 字节“id”实现导致不同部分长度的 uint32 实例化和移位。我希望只使用本机 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中数值类型与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和bytes的格式是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')]