Russian Qt Forum

Qt => Вопросы новичков => Тема начата: __Heaven__ от Май 17, 2013, 06:09



Название: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 17, 2013, 06:09
Всем снова привет!
Есть структура, которую я записываю в бинарный файл с помощью QDataStream::writeRawData.
Отдельно выписываю каждый QString через оператор <<.
При чтении этого файла, чтение структуры проходит успешно. А вот при чтении в QString выдает ошибку (realloc или чё-то там...)
Вскоре понял, что это происходит из-за того значения, которое передаётся в QString при чтении файла. Решил проблему с помощью QString::clear() и последующим чтением QString из файла. Только теперь уже проблема возникла иная: при закрытии программы выскакивает что-то вроде  heap corruped.
Вопрос: какое именно значение из QString записывается при сериализации и как при десериализации избежать переписывания QString переменных или "сохранить нынешние значения QString (которое затирается при деериализации) -> прочитать файл -> восстановить это значение)
Спасибо


Название: Re: Сериализация QString содержащей структуры.
Отправлено: xokc от Май 17, 2013, 11:56
Трижды перечитал вопрос - так и не понял про что он. Давай, что-ли с публикации проблемного кода начнем?


Название: Re: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 18, 2013, 11:27
Сама структура:
Код:
#ifndef SOLUTIONSETTINGS_H
#define SOLUTIONSETTINGS_H

class QString;

struct ComponentSettings
{
QString name;
double concentration;
double liquidusTangent;
double distributionRatio;
};

struct SolutionSettings
{
int substrateHeight;
int addingLayerHeight;
int addingLayerCount;
double areaWidth;
double areaDepth;
int NodesPerSize;
int laserSpotDiameter;
int laserPower;
int preheatingTemperature;
int gasTemperature;
int laserSpeed;
int distanceBetweenPasses;
int penetrationRatio;
int delayAfterHeating;
QString wayType;
double delayBetweenPasses;
double calculationTimeStep;
int outputTimeStep;
QString materialName;
double liquidusTemperature;
double coresCount;
double growSpeedRatio;
double interaxialDistance;
double porosity;
double particlesDiameter;
ComponentSettings component1,
  component2,
  component3;
};

#endif

Её записываем в файл.
Код:
	out.writeRawData(reinterpret_cast<char*>(&_settings), sizeof(_settings));

out << _settings.wayType
<< _settings.materialName
<< _settings.component1.name
<< _settings.component2.name
<< _settings.component3.name;
binFile.close();
где
Код:
	SolutionSettings _settings;
и
Код:
	QFile binFile(slmFilePath);
binFile.open(QIODevice::WriteOnly);
QDataStream out(&binFile);


Название: Re: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 18, 2013, 11:42
При чтении уже в другой программе:
Код:
	in.readRawData(reinterpret_cast<char*>(&settings), sizeof(settings));
где
Код:
	QFile file(filePath);
file.open(QIODevice::ReadOnly);
QDataStream in(&file);

для чтения строк изначально написал
Код:
	in >> settings.wayType;
in >> settings.materialName;
in >> settings.component1.name;
in >> settings.component2.name;
in >> settings.component3.name;
но, этот код выдавал ошибку при выполнении первой функции. Была какая-то проблема с распределением памяти. Но её я обошёл тем, что для каждой из читаемой строки выполнил метод QString::clear(). Всё пошло гладко, но только по завершению работы приложения выдается ошибка heap corruption detected...

То есть, я полагаю, что при чтении из файла целиком структуры, в элементы QString были записаны какие-то произвольные значения (которые были рабочими при записи в файл). Таким образом, эти значения дают понять, что при выполнении некого деструктора (который выполняется по завершению программы) производится очистка памяти по тем самым значениям, которые были вписаны в структуру из файла, но они являются не актуальными, так как не относятся к выполняемому процессу, а просто были считаны из файла.


Название: Re: Сериализация QString содержащей структуры.
Отправлено: thechicho от Май 18, 2013, 11:48
а чо QSettings не воспользуетесь?


Название: Re: Сериализация QString содержащей структуры.
Отправлено: mutineer от Май 18, 2013, 11:52
Писать QString в виде бинарного потока в одном процессе и вычитывать в другом это очень опасно, он наверняка имеет какие-то внутренние указатели, которые в другой программе невалидны. Не делай так, записывай всю структуру через QDataStream и операторы записи в поток


Название: Re: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 18, 2013, 12:36
а чо QSettings не воспользуетесь?
Так как это не единственное, что я записываю в файл. Туда ещё идёт свыше 1000 QVector

Писать QString в виде бинарного потока в одном процессе и вычитывать в другом это очень опасно, он наверняка имеет какие-то внутренние указатели, которые в другой программе невалидны. Не делай так, записывай всю структуру через QDataStream и операторы записи в поток
вот я про эти указатели и говорю. Как-то мучительно получается по отдельности записывать и считывать каждый элемент в отдельности + при добавлении элементов в структуру нужно искать чтение и запись и снова добавлять... Уверен, что должен быть какой-то другой способ. У меня появились некоторые идеи, сейчас попробую их спрораммировать, позже отпишусь по результатам, может кому полезно будет


Название: Re: Сериализация QString содержащей структуры.
Отправлено: mutineer от Май 18, 2013, 12:40
Перегрузи операторы << и >> для своей структуры и тогда все будет выглядеть просто как
Код
C++ (Qt)
out << settings;
...
in >> settings;
 

и при изменении структуры поменять надо будет только эти два оператора


Название: Re: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 18, 2013, 14:52
Нашёл выход, более подходящий для меня!
Запись структуры оставить такой же, как я описывал выше. Но, конечно, как в моём случае вписываются лишние 5 * sizeof(QString) байт - думаю, для меня это не критично.

Перед чтением файла нужно сохранить значения в каждом из QString. А после чтения восстановить их на прежние места.
Иными словами:
Код:
	char* strings[] = 
{
reinterpret_cast<char*>(&settings.wayType),
reinterpret_cast<char*>(&settings.materialName),
reinterpret_cast<char*>(&settings.component1.name),
reinterpret_cast<char*>(&settings.component2.name),
reinterpret_cast<char*>(&settings.component3.name)
};
char oldValues[sizeof(strings) / sizeof(char*) * sizeof(QString)];
for (int i = 0; i < sizeof(strings) / sizeof(char*); i++)
{
char* string = strings[i];
for (int j = 0; j < sizeof(QString) / sizeof(char); j++)
{
oldValues[sizeof(QString) / sizeof(char) * i + j] = string[j];
}
}

in.readRawData(reinterpret_cast<char*>(&settings), sizeof(settings));

for (int i = 0; i < sizeof(strings) / sizeof(char*); i++)
{
char* string = strings[i];
for (int j = 0; j < sizeof(QString) / sizeof(char); j++)
{
string[j] = oldValues[sizeof(QString) / sizeof(char) * i + j];
}
}

in >> settings.wayType;
in >> settings.materialName;
in >> settings.component1.name;
in >> settings.component2.name;
in >> settings.component3.name;
в середине можно было использовать memcpy, но я его не особо люблю.
Теперь изменение структуры никак не повлияет на код чтения/записи. Если не трогать классы.
Спасибо всем за советы.


Название: Re: Сериализация QString содержащей структуры.
Отправлено: Igors от Май 18, 2013, 15:04
Впечатление "хоть говори - хоть стреляЙ"  :) Просто-напросто НЕ используйте readRawData/writeRawData, они только для сишных структур (и то не всегда). Пишите/читайте QString операторами <<  >>  - это все что нужно.

[/offtop]Не, не дойдет  :'(


Название: Re: Сериализация QString содержащей структуры.
Отправлено: mutineer от Май 18, 2013, 15:19
Ужас какой...


Название: Re: Сериализация QString содержащей структуры.
Отправлено: Fregloin от Май 23, 2013, 09:19
да уж, код ужасен.
я всегда пишу C-строки в бинарные файлы для совместимости с другими прогами, которые написаны не на Qt/C++ (Delphi например).


Название: Re: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 30, 2013, 11:19
Да, согласен, что код ужасен.

А в этой же теме хочу спросить по поводу writeRawData.
Каким образом, не используя эту функцию можно записать данные (особенно double), чтобы они в последствии могли быть считаны в среде разработки delphi?


Название: Re: Сериализация QString содержащей структуры.
Отправлено: xokc от Май 31, 2013, 12:36
Код
C++ (Qt)
double value = 123.456;
out << value;

Код
Delphi
Value: Double;
Stream.Read(@Value, SizeOf(Value));


Название: Re: Сериализация QString содержащей структуры.
Отправлено: Igors от Май 31, 2013, 13:31
Дополняя предыдущий ответ: перед записью надо установить QDataStream::setByteOrder с аргументом чтобы понравился дельфи (вероятно QDataStream::LittleEndian)


Название: Re: Сериализация QString содержащей структуры.
Отправлено: __Heaven__ от Май 31, 2013, 20:00
Дополняя предыдущий ответ: перед записью надо установить QDataStream::setByteOrder с аргументом чтобы понравился дельфи (вероятно QDataStream::LittleEndian)

Да, согласен. Вышеизложенное я изначально пробовал воплотить - не понравилось.