i have a QByteArray variable like this:
QByteArray read= port->readAll();
now i want convert read to Array for write binary file like this:
int b[] = {}; // lengh of array is port->readAll() size
QFile myFile("e:/test/test.dat");
if(!myFile.open(QIODevice::WriteOnly))return;
myFile.write((char*)b,sizeof(int));
myFile.flush();
myFile.close();
No need to create int[] b = ..etc
Just use either the method QByteArray::data():
QFile file(...);
QByteArray byteArray = ...
...
file.write(byteArray.data(), byteArray.size());
Or you can set the QByteArray's object directly to file.write():
QFile file(...);
QByteArray byteArray = ...;
file.write(byteArray);
Related
I have a splitter and I want to save his state in file with JSON.
QJsonObject mainJson;
// here I have to convert QByteArray to QString
mainJson.insert("test", QTextCodec::codecForMib(1015)->toUnicode(ui->splitter->saveState()));
QFile file("test.json");
QTextStream textStream;
file.open(QFile::WriteOnly);
textStream.setDevice(&file);
textStream.setCodec("UTF-8");
textStream << QString(QJsonDocument(mainJson).toJson()).toUtf8();
textStream.flush();
file.close();
But file contains this:
\u0000\u0000Ā\u0000Ȁ\u0000Ⰱ\u0000쐀\u0000\u0000Ā\u0000Ȁ
Is that ok? And how to convert this back to QByteArray for ui->splitter->restoreState(...);?
PS: I use code from here
The logic in general is to convert the QByteArray to QString, in this case I prefer to convert it to base64 than to use a codec for unicode to avoid the problems of compression and decompression.
Considering the above, the solution is:
Save:
QJsonObject mainJson;
QByteArray state = spliter->saveState();
mainJson.insert("splitter", QJsonValue(QString::fromUtf8(state.toBase64())));
QFile file("settings.json");
if(file.open(QIODevice::WriteOnly)){
file.write(QJsonDocument(mainJson).toJson());
file.close();
}
Restore:
QJsonDocument doc;
QFile file("settings.json");
if(file.open(QIODevice::ReadOnly)){
doc = QJsonDocument::fromJson(file.readAll());
file.close();
}
if(doc.isObject()){
QJsonObject obj = doc.object();
QByteArray state = QByteArray::fromBase64(obj.value("splitter").toString().toUtf8());
spliter->restoreState(state);
}
I am trying to log data from 3 sensors to a json file. All I want to be able to accomplish is to write the Speed, Latitude and Longitude to a Json file, with an object containing each of the above. That is a json file that contains one route object, n sub objects each of which contain speed, latitude, longitude.
These 3 values I get from 3 global QList lists. Below is the json file which is stored locally. (The double values are not actual values, just for testing purposes)
{
"Sensordata": [
{
"Speed": 1,
"GPSLat":-12.5687,
"GPSLong":26.125546
},
{
"Speed": 1,
"GPSLat":-12.5687,
"GPSLong":26.125546
}
]
}
This is what the json must look like and when I add it must be formatted in the same way
void MainWindow::save_to_json() {
QFile file_obj(".../SensorData.json");
if(!file_obj.open(QIODevice::ReadOnly)){
qDebug()<<"Failed to open "<<"SensorData.json";
exit(1);
}
QTextStream file_text(&file_obj);
QString json_string;
json_string = file_text.readAll();
file_obj.close();
QByteArray data_json = json_string.toLocal8Bit();
QJsonDocument doc = QJsonDocument::fromJson(data_json);
QJsonObject rootObj = doc.object();
QJsonValue SensorData = rootObj.value("SensorData");
if(!SensorData.isArray())
{
// array expected - handle error
}
QJsonArray SensorDataArray = SensorData.toArray();
QJsonObject newObject;
newObject["Speed"] = speed_array.takeFirst();
newObject["GPSLat"] = gps_lat.takeFirst();
newObject["GPSLong"] = gps_long.takeFirst();
SensorDataArray.push_back(newObject);
}
ASSERT: "!isEmpty()" in file /home/username/Qt/5.12.1/gcc_64/include /QtCore/qlist.h, line 347
11:32:55: The program has unexpectedly finished.
11:32:55: The process was ended forcefully.
This is the error the above code creates.
To modify the data, given your example, you need to check if the contained data in the QJsonDocument is an array or a simple object. In your case, I suppose you want to append data to an array. Try something like this:
// Read the data
const QString filename = "example.json";
QJsonDocument doc = read(filename);
// Check that it's an array and append new data
QJsonValue sensorData = doc.value("SensorData");
if (!sensorData.isArray()) {
// if the doc is empty you may want to create it
}
// Get the array and insert the data
auto array = sensorData.array();
array.append(QJsonObject{
{"Speed", speed_array.takeFirst()},
{"GPSLat", gps_lat.takeFirst()},
{"GPSLong",gps_long.takeFirst(),
});
// Restore your sensor data
doc.setObject(QJsonObject{{"SensorData", array}});
// Write the new data
write(filename, doc);
A helper functions to read/write JSON documents may avoid the mistake of open/closing a file:
QJsonDocument read(const QString& filename) {
QFile file(filename);
file.open(QIODevice::ReadOnly | QIODevice::Text);
const QString val = file.readAll();
file.close();
return QJsonDocument::fromJson(val.toUtf8());
}
void write(const QString& filename, const QJsonDocument& document) {
QFile file(filename);
file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
file.write(document.toJson());
file.close();
}
Updates
To not overwrite the original doc, you must update the field of the root object or use QJsonValueRef.
// Get a reference to your array
QJsonObject root = doc.object();
QJsonValueRef ref = root.find("SensorData").value();
// get the array and insert the data
QJsonArray array = ref.toArray();
array.append(QJsonObject{
{"Speed", speed_array.takeFirst()},
{"GPSLat", gps_lat.takeFirst()},
{"GPSLong",gps_long.takeFirst(),
});
// Update the ref with the new data
ref = array
// update the modified data in the json document
doc.setObject(root);
I am a newbie, I am creating a XML file in which I need to give CRC value to cross check on server. For this I need QDomDocument data as text. For this I am first creating XML file with fake CRC value. Then I open it with QFile and read all data. Then I split data and calculate data CRC. Now I have to rewrite whole file again. I know it is the worst idea ever to write same file twice but as I am a newbie, I don't know how to do it in better style. Here is my code:-
QDomElement docElem = doc.documentElement();
QFile xmlfile(filename);
if(!xmlfile.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug("Can not open file device.");
}
xmlfile.resize(0);
QXmlStreamWriter xw;
xw.setDevice(&xmlfile); //set file to XML writer
xw.setAutoFormatting(true);
xw.setAutoFormattingIndent(4);
xw.writeStartDocument();
xw.writeStartElement(fileID); //fileID as the start element
if(docElem.hasAttributes())
{
xw.writeAttribute("xmlns:xs",docElem.attribute("xmlns:xs"));
xw.writeAttribute("xmlns",docElem.attribute("xmlns"));
}
xw.writeTextElement("Frame_Start_ID","STX");
xw.writeTextElement("Frame_Length","1234");
xw.writeTextElement("Source_Device_Id","CDIS_PIS ");
xw.writeTextElement("Destination_Device_Id","DDNS-SERVER ");
xw.writeTextElement("Frame_Code","I");
xw.writeStartElement("Frame_Data");
//inside frame data
xw.writeTextElement("File_Number","1");
xw.writeTextElement("File_Name","Event");
for(int j=0;j<logFields.count();j++)
{
xw.writeTextElement(logFields.at(j),logData.at(j));
}
xw.writeEndElement();
xw.writeTextElement("CRC","14405904");
xw.writeTextElement("Frame_End_Id","ETX");
xw.writeEndDocument();
xmlfile.flush();
xmlfile.close();
QFile xmlfyle(filename);
xmlfyle.open(QIODevice::ReadWrite | QIODevice::Text);
QString content = (QString)xmlfyle.readAll();
QStringList list1 = content.split("<CRC>");
qDebug() << "Split value = " << list1.at(0);
QByteArray crc_new = crc_o.crc_generate_modbus((unsigned char*)list1.at(0).data(),list1.at(0).size());
xmlfyle.resize(0);
QXmlStreamWriter xw_new;
xw_new.setDevice(&xmlfyle); //set file to XML writer
xw_new.setAutoFormatting(true);
xw_new.setAutoFormattingIndent(4);
xw_new.writeStartDocument();
xw_new.writeStartElement(fileID); //fileID as the start element
if(docElem.hasAttributes())
{
xw_new.writeAttribute("xmlns:xs",docElem.attribute("xmlns:xs"));
xw_new.writeAttribute("xmlns",docElem.attribute("xmlns"));
}
xw_new.writeTextElement("Frame_Start_ID","STX");
xw_new.writeTextElement("Frame_Length","1234");
xw_new.writeTextElement("Source_Device_Id","CDIS_PIS ");
xw_new.writeTextElement("Destination_Device_Id","DDNS-SERVER ");
xw_new.writeTextElement("Frame_Code","I");
xw_new.writeStartElement("Frame_Data");
xw_new.writeTextElement("File_Number","1");
xw_new.writeTextElement("File_Name","Event");
for(int j=0;j<logFields.count();j++)
{
xw_new.writeTextElement(logFields.at(j),logData.at(j));
}
xw_new.writeEndElement();
char tab[10];
sprintf(tab,"%d",crc_new.data());
xw_new.writeTextElement("CRC",QString::fromUtf8(tab));
xw_new.writeTextElement("Frame_End_Id","ETX");
xw_new.writeEndDocument();
xmlfyle.flush();
xmlfyle.close();
can anyone suggest me what could be a better way to do this.Thanks
One version of QXmlStreamWriter constructor accepts a QByteArray and writes into the array instead of an output file.
QXmlStreamWriter Class
So what you can do is; using QXmlStreamWriter, prepare data for your XML in a QByteArray, do whatever you need to do with the CRC inside this data; and when everything is done, write this QByteArray to the output file.
I need to serialize and parse multiple objects from my project, in order to save/load them when needed.
My objects will have exactly the same components : a QString name, an integer id, a QString description, and two integer x, y.
I'll need something like this :
{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
So I'll build my QJsonObject like this :
QJsonObject json;
json["id"] = object_to_serialize.get_id();
json["name"] = object_to_serialize.get_name();
json["description"] = object_to_serialize.get_description();
json["x"] = object_to_serialize.get_x();
json["y"] = object_to_serialize.get_y();
QJsonDocument filedoc(json);
file.write(filedoc.toJson);`
And in the file it will appear like this :
{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}
{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}
My serialiser will take in parameter the object, the savefile name, and transform the object into a QJsonObject. It will need then to read the file to check if an object with the same id is here. If it is here, it will need to replace it, and if it is not, it will append it.
I'm a little lost between my serialization options and how to read it ;
Should I make a QJsonArray with multiple QJsonObject inside or QJsonObject with QJsonArrays ?
When I read it, I will need to check for the id ; but will a
foreach(object.value["id"] == 42)
//create the QJsonObject from the one with 42 and change it with the new data
will do to parse the object and not all of them ? Is there a better way ?
Thank you in advance for your answers.
You can have an array of json object, each of them having an ID so you can parse the relevant ones.
Although you could also parse all of them and add them in a map, as long as you don't have very heavy files it should be fine.
void parseJson(const QString &data)
{
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
if (doc.isNull())
{
war("invalid json document");
return;
}
QJsonArray jsonArray = doc.array();
foreach (const QJsonValue & value, jsonArray) {
QJsonObject obj = value.toObject();
if (obj.contains("id"))
{
if (obj["id"].toInt() == yourId) parseObject(obj);
}
}
}
void parseObject(const QJsonObject &obj)
{
if (obj.contains("valueA")) valueA = obj["valueA"].toDouble();
if (obj.contains("valueB")) valueB = obj["valueB"].toDouble();
}
This will work just fine if your file is not too big
Bigger Files
Now if you have very large file, it might be an issue to load it all in memory and parse it.
Since your structure is always the same and quite simple, JSON might not be the best choice, one more efficient method would be to do your own parser (or use probably some existing ones) that could read the file and process it as a stream.
Another method, would be to have one JSON entry per line preceded by an ID with a fixed number of digit. Load this in a QHash lookup and then only read id of interest from the file and only parse a small section.
// This code is not tested and is just to show the principle.
#define IDSIZE 5
QHash<int64, int64> m_lookup; // has to be global var
// For very large file, this might take some time and can be done on a separate thread.
// it needs to be done only once at startup (given the file is not modified externally)
void createLookup(const QString &fileName)
{
QFile inputFile(fileName);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while (!in.atEnd())
{
int position = in.pos(); // store the position in the file
QString line = in.readLine();
int id = line.mid(0,IDSIZE).toInt(); // 5 digit id (like 00001, 00002, etc...
m_lookup[id] = position + IDSIZE;
}
inputFile.close();
}
}
QString getEntry(const QString &fileName, int64 id)
{
if (m_lookup.contains(id))
{
QFile inputFile(fileName);
if (inputFile.open(QIODevice::ReadOnly))
{
inputFile.seek(m_lookup[id]);
QString data = inputFile.readLine();
inputFile.close();
return data;
} else {
return QString(); // or handle error
}
} else {
return QString(); // or handle error
}
}
// use example
QString data = getEntry(id);
if (data.length() > 0)
{
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
if (!doc.isNull())
{
// assign your variables
}
}
and your data file looking like this:
00042{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
00044{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}
00046{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}
The advantage of this method, it will only read the entry of interest, and avoid having to load MB or GB of data in memory just to get a specific entry.
This could further be improved with a lookup table stored at the beginning of the file.
I am making a file editor in Qt C++.
I noticed that when I save the source file with notepad on Windows it substitutes 0x0D + 0x0A for every new line (ANSI encoding default).
If I get the data from a QTextEdit and convert that to a QByteArray it substitutes line endings with 0x0A.
Now my main problem is that I don't know how to either filter out these line endings or make it so that QFile reads files different.
I just want a way of comparing whether there are changes that need to be saved.
The code is below:
bool QFileTab::needSave()
{
QFile file1 ( this->filePath );
file1.open(QIODevice::ReadOnly);
QByteArray data1 ( file1.readAll() );
QByteArray data2 ( this->body.toPlainText().toLocal8Bit() );
file1.close();
return !IOHandler::compare(data1, data2);
}
void IOHandler::read(QFileTab *tab)
{
QFile temp (tab->filePath);
if(temp.open(QIODevice::ReadOnly | QIODevice::Text))
{
QString data(temp.readAll());
tab->data = data;
temp.close();
} else tab->data = QString("The file could not be read.");
tab->doUpdate();
}
bool IOHandler::compare(QByteArray data1, QByteArray data2)
{
return data1 == data2;
}
I want to be able to read, compare and write to files on Windows and Linux.