Russian Qt Forum

Qt => Общие вопросы => Тема начата: Larry от Апрель 22, 2016, 13:13



Название: [РЕШЕНО]Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 13:13
Добрый день.
Есть бинарный файл в который записаны данные из структуры на delphi.
Вот структура:
Код:
TData=record
      id:string[10];
      comment:string[200];
      primarySensor,kindAnalysisSensor:byte;
      countPointData:integer;
      v,u,a,b:array of real;
      percentTopLevel,percentBottomLevel:real;
end;

Как мне прочитать эти данные?


Название: Re: Чтение бинарного файла
Отправлено: Igors от Апрель 22, 2016, 14:14
Вот они
Цитировать
ребята на QML сто двадцать плюс
:) Все-таки лучше перенести это в самый важный (первый) раздел форума.

А серьезно - начните с дельфей и установите там правила записи которые здесь будет соблюдать чтение. Без этого не обойтись, напр если там ребята пишут через sizeof, то Ваше чтение развалится при смене установок дельфийского компилятора. Также как пишутся массивы переменной длины и кто такой real (4, 6 или 8 байт) там надо разбираться. Также хорошим тоном считается запись маркера, числа байт, и обычно еще версии.


Название: Re: Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 22, 2016, 14:20
Larry, читать бинарные файлы можно 2 способами. Либо через QFile напрямую, либо через QDataStream.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 15:14
Спасибо, пытался через QDataStream::readRawData и считал в char id[10] первые 10 символов, но потом вывел массив char через qDebug(), а там ахинея...Вывел в цикле id по символу, то увидел id + мусор, а в qstring так и не получилось.


Название: Re: Чтение бинарного файла
Отправлено: Bepec от Апрель 22, 2016, 15:20
Вам нужно
1) установить правила записи в бинарный файл, чтобы запись была независима от настроек компилятора. (если проще - самому написать ф-цию которая побайтно будет записывать данные)
2) сделав 1 пункт, вы получите бинарный файл с знакомой структурой данных.
3) зная эту структуру вы сможете на любом языке прочитать эти данные.

Ну и да, КО отвечает - чтобы прочитать данные, нужно взять QFile, считать все данные в QByteArray и потом уже разбирать и запихивать в Qt-шные типы.


Название: Re: Чтение бинарного файла
Отправлено: Hellraiser от Апрель 22, 2016, 15:21
string в Паскале и массив char используют разный подход для хранения данных. В Паскале нулевой элемент хранит количество символов в строке.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 15:46
Спасибо, но к исходникам программы нет доступа и никто там для меня ничего не изменит ;D, значит пойду другой дорогой.


Название: Re: Чтение бинарного файла
Отправлено: Igors от Апрель 22, 2016, 16:21
Спасибо, пытался через QDataStream::readRawData и считал в char id[10] первые 10 символов, но потом вывел массив char через qDebug(), а там ахинея...Вывел в цикле id по символу, то увидел id + мусор, а в qstring так и не получилось.
Покажите пример двоичных данных (можно файл, можно скриншот hex-viewera), а то по пустому месим.

Спасибо, но к исходникам программы нет доступа и никто там для меня ничего не изменит ;D,
Тогда надо иметь ввиду что Ваше приложение легко может оказаться несовместимым со след версией пишущего

..значит пойду другой дорогой.
Цитировать
- Аааа.. ведь я не сказал ему адреса!!!
- А здесь одна дорога
(классика советского кино)


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 16:36
Сделал скрин.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 22, 2016, 16:49
Larry, а можно и сам файлик?


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 22, 2016, 16:50
И данные, которые ожидается от него получить. Хотя бы первый элемент.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Old от Апрель 22, 2016, 16:53
Первые 4 байта это скорее всего 32 битное число - наверное кол-во таких структур в файле.
Дальше идут два текстовых поля, их формат первый байт кол-во символов, дальше 10 и 200 байт для сохранения строки соответственно. Булевые значения скорее всего храняться в одном байте каждый. real это 8 битное число с плавающей точкой (double). Массив реалов скорее всего хранится как кол-во элементов (32-бита - не уверен лень считать на экране байты), а дальше сами элементы. Структуры идут последовательно одна за другой. Читается это все просто с помощью QDataStream.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 22, 2016, 17:00
Old, а можно пример?
У меня есть подобная задача и я прям чувствую, что криво считываю данные. Больше всего смущает, что QDataStream использует version. И не совсем понятно, как читать массивы, циклом?


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 17:02
Сам файл предоставить не могу...низя:)
Там хранятся данные из массива структур, которую я описал в первом посте...


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 22, 2016, 17:05
Сам файл предоставить не могу...низя:)
Ну вы уже предоставили его часть :D Можно обрезать на 02f0 и поделиться :)


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Old от Апрель 22, 2016, 17:11
Old, а можно пример?
У меня есть подобная задача и я прям чувствую, что криво считываю данные. Больше всего смущает, что QDataStream использует version. И не совсем понятно, как читать массивы, циклом?
Пример чтения? Так там все просто. Массивы QDataStream сохраняет вначале кол-во элементов (quint32), а затем сами элементы. Все это описано в документации.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 17:12
Тоже верно:). Сейчас создам другой и выложу...


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Igors от Апрель 22, 2016, 17:15
У меня есть подобная задача и я прям чувствую, что криво считываю данные. Больше всего смущает, что QDataStream использует version. И не совсем понятно, как читать массивы, циклом?
QDataStream по умолчанию читает в большой индиане, а здесь маленькая, используйте QDataStream::setByteIrder(QDataStream::LittleEndian). Со строками ясно, с массивами - сделайте запись где по 1 (или 2) эл-та в каждом, тогда прояснится


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 17:18
Вот файл...


Название: Re: Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 22, 2016, 17:59
Пример чтения? Так там все просто. Массивы QDataStream сохраняет вначале кол-во элементов (quint32), а затем сами элементы. Все это описано в документации.
У меня тоже файл, полученный из другой программы, спецификацию которого я разгадывал при помощи читалки этого файла и самописного hex просмотрщика. И там размерности массива не стоят перед данными. Там примерно такая запись: блаблаблаРАЗМЕРМАССИВАблаблаблаXXXYYYZZZ.


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 22, 2016, 18:28
У меня тоже файл, полученный из другой программы, спецификацию которого я разгадывал при помощи читалки этого файла и самописного hex просмотрщика. И там размерности массива не стоят перед данными. Там примерно такая запись: блаблаблаРАЗМЕРМАССИВАблаблаблаXXXYYYZZZ.
И вы не знаете для чего поля блаблабла?
Сложно так сказать... это могут быть случайные данные оставшиеся от выравнивания полей структуры или маркеры данных или ... :)


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 22, 2016, 20:45
Есть бинарный файл в который записаны данные из структуры на delphi.
Смотрю я на данные и что-то не сходится. Судя по данным похоже, что структура чуть другая - поля percentTopLevel и percentBottomLevel расположены не в конце, а после сенсоров:

Код:
TData=record
      id:string[10];
      comment:string[200];
      primarySensor,kindAnalysisSensor:byte;
      percentTopLevel,percentBottomLevel:real;
      countPointData:integer;
      v,u,a,b:array of real;
end;

Уточните у разработчиков программы-писателя.


Название: Re: Чтение бинарного файла
Отправлено: gil9red от Апрель 22, 2016, 21:08
Пример чтения? Так там все просто. Массивы QDataStream сохраняет вначале кол-во элементов (quint32), а затем сами элементы. Все это описано в документации.
У меня тоже файл, полученный из другой программы, спецификацию которого я разгадывал при помощи читалки этого файла и самописного hex просмотрщика. И там размерности массива не стоят перед данными. Там примерно такая запись: блаблаблаРАЗМЕРМАССИВАблаблаблаXXXYYYZZZ.

Может, это был TLV формат: тег длина значение
тогда, блаблабла -- это тег


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 22, 2016, 22:59
эта структура выдернута из исходников и порядок я не менял... Судя по структуре, то percentTopLevel и percentBottomLevel и не находятся в конце?! или я не так понял.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 07:56
Не понятно, как читать:)...судя по hex там в начале идет 5 байт, а потом уже идет id - 10 байт и следом comment - 200 байт. По структуре первое поле id. Непонятно...


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 08:01
Не понятно, как читать:)...судя по hex там в начале идет 5 байт, а потом уже идет id - 10 байт и следом comment - 200 байт. По структуре первое поле id. Непонятно...
Я же вам на первой странице все расписал.  ::)


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 08:16
Я попытался прочить в int и получил 16777216, т.е. это явно не int.


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 08:20
Я попытался прочить в int и получил 16777216, т.е. это явно не int.
Первые четыре байта это 32-битное число в little-endian, поэтому для его корректного чтения, нужно QDataStream переключить на использование этого эндиана:
void QDataStream::setByteOrder(ByteOrder bo)


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 08:22
Спасибо, сейчас попробую.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 08:29
Да, сейчас при чтении в int получаю единицу, там как раз одна структура. А как из QDataStream прочитать в QString?


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 08:34
прочитал в массив char[10] с помощью readRawData и получил следующее #73397?v #6. Там где #, то там выводятся элементы псевдографики. В id должны быть только цифры и в данном случае это 73397. И перед id остается 1 байт, который равен у меня 5.


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 08:37
Да, сейчас при чтении в int получаю единицу, там как раз одна структура. А как из QDataStream прочитать в QString?
Нужно прочитать в QByteArray размер строки (10 или 200) + 1 байт. 1 байт содержит реальное кол-во символов в строке, дальше идут сами символы. В какой кодировке эти символы нужно узнать у писателя. А дальше массив символов можно будет преобразовать в QString через QTextCodec, используя QTextCodec::toUnicode.


Название: Re: Чтение бинарного файла
Отправлено: Igors от Апрель 23, 2016, 09:04
А как из QDataStream прочитать в QString?
Код
C++ (Qt)
QDataStream strm;
strm.setByteOrder(..);
...
int count;
strm >> count;
char buf[11];
strm.readRawData(buf, 11);
QString test(QByteArray(buf + 1, buf[0]));
qDebug() << test;


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 09:09
Код
C++ (Qt)
QDataStream strm;
QString test(QByteArray(buf + 1, buf[0]));
 
Не уверен, что строки там в utf-8.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:17
А как из QDataStream прочитать в QString?
Код
C++ (Qt)
QDataStream strm;
strm.setByteOrder(..);
...
int count;
strm << count;
char buf[11];
strm.readRawData(buf, 11);
QString test(QByteArray(buf + 1, buf[0]));
qDebug() << test;

Да спасибо, так сработало:)


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 09:18
Да спасибо, так сработало:)
Вы попробуйте с русскими комментариями... ;)


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:21
Да с русскими там вопросительные знаки:). Тогда через QTextCodec?


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 09:22
Да с русскими там вопросительные знаки:). Тогда через QTextCodec?
Тогда да. :)


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:24
Хорошо, спасибо. Попробую дальше прочитать.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:27
Сделал так:
Код:
QTextCodec* codec = QTextCodec::codecForName("windows-1251");
qDebug() << codec->toUnicode(buf) << ", " << codec->toUnicode(buf);

выдает: "\u000573397єv \u0016k\u000573397єv \u0016k"


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 09:29
Сделал так:
А buf как получается? И сразу дамп данных покажите который читаете.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:34
То я обшибся, вывел 2 раза buf, теперь выводит:
"\u000573397єv \u0016ku001Cкомментарий (200 символов)\r\n \u0016k\tЂш\u0012"


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:40
А как избавиться \u0005 и от других символов?


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 09:43
А как избавиться \u0005 и от других символов?
Я же чуть выше попросил вас показать как вы получаете buf и дамп данных, которые читаете в данный момент.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:45
Код:
QDataStream stream(&file);
stream.setByteOrder(QDataStream::LittleEndian);

int count;
stream >> count;
char buf[11];
stream.readRawData(buf, 11);
char buf2[201];
stream.readRawData(buf2, 201);

QTextCodec* codec = QTextCodec::codecForName("windows-1251");
file.close();

QMessageBox::about(this, "title", codec->toUnicode(buf) + ", " + codec->toUnicode(buf2));

то что выводит на изображении.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 09:49
а вот сам файл


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 09:51
Код
C++ (Qt)
QDataStream stream(&file);
stream.setByteOrder(QDataStream::LittleEndian);
 
int count;
stream >> count;
char buf[11];
stream.readRawData( buf, sizeof(buf) );
char buf2[201];
stream.readRawData( buf2, sizeof(buf2) );
 
QTextCodec* codec = QTextCodec::codecForName("windows-1251");
file.close();
 
QMessageBox::about(this, "title", codec->toUnicode( QByteArray( buf + 1, buf[ 0 ] ) ) + ", " + codec->toUnicode( QByteArray( buf2 + 1, buf2[ 0 ] ) ) );
 


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 10:06
Спасибо большое. Буду дальше разбираться.


Название: Re: Чтение бинарного файла
Отправлено: Igors от Апрель 23, 2016, 10:39
В "С" нет длины строки, еe конец определяется нулем на конце. Чтобы избавиться от мусора Вам надо обеспечить этот ноль
Код
C++ (Qt)
char buf[12];
stream.readRawData(buf, 11);
Q_ASSERT(buf[0] < 11);
buf[buf[0]] = 0;
 
char buf2[202];
stream.readRawData(buf2, 201);
Q_ASSERT(buf2[0] < 201);
buf2[buf2[0]] = 0;
 
Обратите внимание что размер массивов теперь + 2 байта (один на длину, другой на завершающий ноль).



Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 11:07
Да спасибо, учту. Прочел два поля primarySensor,kindAnalysisSensor:byte; в quint8 и получил 2 единицы, а в countPointData:integer; читаю в int и получаю 0, хотя там должно быть 11.


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 11:09
Да спасибо, учту. Прочел два поля primarySensor,kindAnalysisSensor:byte; в quint8 и получил 2 единицы, а в countPointData:integer; читаю в int и получаю 0, хотя там должно быть 11.
Я же выше вам писал, про то что поля в файле переставлены. Вначале читайте два double, а потом уже будет int и массивы double.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 11:28
Там должно быть 2 по одному байту вроде бы (судя по структуре) и по названию переменных. Попробовал прочесть два раза double и потом уже количество точек в int:

1.26975e-321 ,  8.09872e-320
737288

придется позже заниматься, т.к. доця на улицу хочет:)


Название: Re: Чтение бинарного файла
Отправлено: Old от Апрель 23, 2016, 11:29
Там должно быть 2 по одному байту вроде бы (судя по структуре) и по названию переменных. Попробовал прочесть два раза double и потом уже количество точек в int:

1.26975e-321 ,  8.09872e-320
737288

придется позже заниматься, т.к. доця на улицу хочет:)
Там идет два по байту, потом два double, потом int, а потом 4 массива double.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 11:32
спасибо, потом попробую и отпишусь.


Название: Re: Чтение бинарного файла
Отправлено: Larry от Апрель 23, 2016, 14:05
Большое-большое спасибо:), все теперь читается - то, что нужно.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 25, 2016, 14:49
Old, я сторонние массивы тоже через readRawData и sizeof читаю. Это оптимальный вариант? Более красивых нет?


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Old от Апрель 25, 2016, 14:54
Old, я сторонние массивы тоже через readRawData и sizeof читаю. Это оптимальный вариант? Более красивых нет?
Красивей только создание класса для этого массива и переопределение операторов << и >>. :)


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 25, 2016, 16:02
Допустим я использую QVector<int> для хранения этих данных. Подозреваю, что наследоваться от QVector с дальнейшим переопределением операторов будет не самой лучшей идеей, ведь так?


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Old от Апрель 25, 2016, 16:06
Допустим я использую QVector<int> для хранения этих данных. Подозреваю, что наследоваться от QVector с дальнейшим переопределением операторов будет не самой лучшей идеей, ведь так?
А стоит пере усложнять? :)
Возможно, будет достаточно сделать функцию для этого?
Например:
Код
C++ (Qt)
bool readData( QVector<int> &data );
 
// или
 
QVector<int> readData( bool &result );
 


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 25, 2016, 16:31
Ну да, не стоит :)


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 25, 2016, 16:34
Спасибо


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Igors от Апрель 25, 2016, 16:52
Код
C++ (Qt)
bool readData( QVector<int> &data );
 
Точно такую же ф-цию придется писать для QVector<float> и.т.п. Поэтому следующий шаг QVectior<T> или даже T & data. Потом выяснится что пошел дубляж в случае "элемент контейнера тоже контейнер". Поэтому лучше сразу продвигать операторы << и >>. И, конечно, без наследования  :)


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: __Heaven__ от Апрель 25, 2016, 17:33
продвигать операторы << и >>. И, конечно, без наследования  :)
Покажите как?


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Bepec от Апрель 25, 2016, 18:04
Видимо по << на каждый тип, добавив ещё класс-контроллер который будет ещё несколько типов разбора файла иметь.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Igors от Апрель 26, 2016, 08:31
Покажите как?
Напр чтение паскалевских строк неплохо оформить так
Код
C++ (Qt)
typedef unsigned char uchar;
 
template <uchar N>
struct TStr {
friend QDataStream & operator >> ( QDataStream & strm, TStr & dst )
{
strm.readRawData((char *) dst.mData, N);
Q_ASSERT(dst.mData[0] <= N);
dst.mData[dst.mData[0]] = 0;
return strm;
}
 
// data
uchar mData[N + 2];
};
 
Уже для 2 строк есть смысл, а будет их больше - тем более. Кстати только сейчас заметил - длина-то unsigned. В итоге чтение всей структуры должно выглядеть так
Код
C++ (Qt)
QDataStream & operator >> ( QDataStream & strm, TData & dst )
{
strm >> dst.id >> dst.comment >> ...  
return strm;
}
 
Большинство операторов >> уже есть для QDataStream. Не проверял, но вероятно и сериализация QVector "совместима" с записанным массивом, но даже если и нет - это прекрасно ляжет под template. В итоге чтение сводится к приятному
Код
C++ (Qt)
QVector<TData> vec;
strm >> vec;


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Old от Апрель 26, 2016, 08:33
Напр чтение паскалевских строк неплохо оформить так

Красивей только создание класса для этого массива и переопределение операторов << и >>. :)

И так как вы оформлять плохо. :)
Для чего хранить буфер для максимально длины строки, если реальных данных может быть значительно меньше. Максимальный буфер можно аллоцировать только в момент чтения (а можно и тогда не аллоцировать), а хранить нужно уже исключительно реальные данные.


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Igors от Апрель 26, 2016, 09:15
Если читать в финальные данные, то примерно так
Код
C++ (Qt)
QDataStream & operator >> ( QDataStream & strm, TData & dst )
{
TStr<10> tempID;
TStr<200> tempComment;
strm >> tempID >> tempComment >> dst. primarySensor >> dst.kindAnalysisSensor >> ..
 
dst.id = tempID.GetQString();
dst.comment = tempComment.GetQString();
...
return strm;
}
Хотя тут исчезает исходная "raw" структура, что нежелательно


Название: Re: [РЕШЕНО]Чтение бинарного файла
Отправлено: Old от Апрель 26, 2016, 10:00
Хотя тут исчезает исходная "raw" структура, что нежелательно
Ну и Бог с ней. Что тут нежелательного? Как правило структуры в памяти несколько шире структур на диске. Добавляются новые поля для удобного хранения, кеширования...