Как открыть файл bin в Python, используя QDataStream

У меня есть файл bin, который был закодирован в приложении, к которому мне нужно получить доступ и преобразовать его в файл csv. Мне дали документацию, но я не уверен, как получить доступ к содержимому этого файла в Python.

Вот некоторые подробности о том, как набор данных был сериализован

Datasets.bin представляет собой список классов DataSet, сериализованных с использованием сериализации QDataStream Qt с использованием версии QDataStream::Qt_4_7.

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, но не могу найти никаких конкретных примеров. Любая помощь, указывающая мне правильное направление, была бы великолепна

1 ответ

Решение

Ниже приведен скрипт, который показывает, как правильно читать формат набора данных.

Однако в настоящее время он не может правильно прочитать все данные. Это из-за QList<double> а также QVector<QPointF> шаблонные классы, для которых PyQt не имеет прямой поддержки. В коде я использовал readQVariantList вместо отсутствующих методов PyQt, которые понадобятся для чтения этих типов.

ОБНОВЛЕНИЕ:

Я спросил об обработке произвольных классов шаблонов в списке рассылки PyQt, и автор PyQt подтвердил, что это невозможно сделать. Таким образом, похоже, что единственный вариант - написать какой-нибудь инструмент преобразования на C++.

ОБНОВЛЕНИЕ 2:

Кажется, я говорил слишком рано, потому что теперь я нашел обходной путь. Формат потока данных намного проще, чем я думал, и поэтому чтение произвольных классов шаблонов может быть сведено к простому алгоритму - в основном, считайте длину как 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))
Другие вопросы по тегам