I have implemented a code that will take input from QLineEdit and the data will be saved in a json file format.
void MainWindow::fileWriteOperationJson()
{
QString filename = "C:/Users/.../Documents/Qt/save.json";
QFile saveFile(filename);
saveFile.open(QIODevice::WriteOnly|QIODevice::Text);
if (!saveFile.open(QIODevice::WriteOnly))
{
qWarning("Couldn't open save file.");
}
QJsonObject obj; //this is the root
QJsonArray personalData;
QJsonObject json;
json["name"] = ui->nameLineEdit->text();
json["address"] = ui->addressLineEdit->toPlainText();
personalData.append(json);
obj["personalData"] = personalData;
QTextStream out(&saveFile);
out << QJsonDocument(obj).toJson(QJsonDocument::Indented);
}
Problem: When I open the json file, I want to find my data in the below format:
"name" = xyz
"address" = xyz
But, I am having result like this,
"address" = xyz
"name" = xyz
How to get this intended order?
JSON (JavaScript Object Notation) is a lightweight data-interchange format and as such, the structure is important, but the order of items is not.
If you need to print the items in a specific order, you'll need to extract them from Json into suitable data structures and handle that yourself.
Alternatively, you could save to a different format, but note that Qt's XML will act the same as Json. Perhaps a CSV may be more useful to you.
Qt generates JSON data with keys sorted alphabetically. AFAIK, there is no option to get around it. You could try encapsulating objects with a single key/value pair into an array, though, and preserve the order:
[
{"address": xyz},
{"name": xyz}
]
Or you could try using a different storage format altogether.
The underlying problem is that QMap does not have an ordered form.
Here's a possible solution by subclassing QVariantMap:
#ifndef ORDEREDVARIANTMAP_H
#define ORDEREDVARIANTMAP_H
#include <QtCore>
class OrderedVariantMap : public QMap<QString, QVariant> {
// Test:
// OrderedVariantMap test_map;
// test_map.insert("xxx", 1);
// test_map.insert("aaa", 2);
// test_map.insert("kkk", 3);
// test_map["321"] = 4;
// test_map["000"] = 5;
// test_map["123"] = 6;
// qDebug() << "QMap.keys()" << test_map.keys();
// qDebug() << "QMap.orderedKeys()" << test_map.orderedKeys();
// QVariant test_variant;
// test_variant.setValue(test_map);
// qDebug() << "test_variant.typeName()" << test_variant.typeName();
// OrderedVariantMap test_map_recovered = qvariant_cast<OrderedVariantMap>(test_variant);
// qDebug() << "test_map_recovered.orderedKeys()" << test_map_recovered.orderedKeys();
// Test results:
// QMap.keys() ("000", "123", "321", "aaa", "kkk", "xxx")
// QMap.orderedKeys() ("xxx", "aaa", "kkk", "321", "000", "123")
// test_variant.typeName() OrderedVariantMap
// test_map_recovered.orderedKeys() ("xxx", "aaa", "kkk", "321", "000", "123")
public:
OrderedVariantMap ( );
~OrderedVariantMap ( );
void
clear ( );
void // QMap::iterator
insert ( const QString &key,
const QVariant &value );
QVariant&
operator[] ( const QString &key );
const QVariant
operator[] ( const QString &key ) const;
const QString
orderedKey ( int index ) const;
const QVariant
orderedValue ( int index ) const;
QStringList
orderedKeys ( ) const ;
private:
QStringList Ordered_Keys;
protected:
};
Q_DECLARE_METATYPE(OrderedVariantMap)
#endif // ORDEREDVARIANTMAP_H
and
#include "OrderedVariantMap.h"
OrderedVariantMap::OrderedVariantMap ( ) : QMap ( ) {
}
OrderedVariantMap::~OrderedVariantMap ( ) {
}
QStringList
OrderedVariantMap::orderedKeys ( ) const {
return Ordered_Keys;
}
void
OrderedVariantMap::clear ( ) {
Ordered_Keys.clear();
QMap::clear();
}
void // QMap::iterator
OrderedVariantMap::insert ( const QString &key,
const QVariant &value ) {
Ordered_Keys.append(key);
QMap::insert(key, value);
}
QVariant&
OrderedVariantMap::operator[] ( const QString &key ) {
Ordered_Keys.append(key);
return QMap::operator [](key);
}
const QVariant
OrderedVariantMap::operator[] ( const QString &key ) const {
return this->value(key);
}
const QString
OrderedVariantMap::orderedKey ( int index ) const {
return Ordered_Keys[index];
}
const QVariant
OrderedVariantMap::orderedValue ( int index ) const {
return this->value(Ordered_Keys[index]);
}
More functionality could be provided, for example an ordered iterator.
Related
I don't understand memory management in C++ Arrow API. I use Arrow 1.0.0 and I'm reading CSV file. After a few runs of ReadArrowTableFromCSV, my memory is full of allocated data. Am I missing something? How can I free that memory? I don't see any method in memory pool to clear all allocated memory. Code Listing below.
void LoadCSVData::ReadArrowTableFromCSV( const std::string & filePath )
{
auto tableReader = CreateTableReader( filePath );
ReadArrowTableUsingReader( *tableReader );
}
std::shared_ptr<arrow::csv::TableReader> LoadCSVData::CreateTableReader( const std::string & filePath )
{
arrow::MemoryPool* pool = arrow::default_memory_pool();
auto tableReader = arrow::csv::TableReader::Make( pool, OpenCSVFile( filePath ),
*PrepareReadOptions(), *PrepareParseOptions(), *PrepareConvertOptions() );
if ( !tableReader.ok() )
{
throw BadParametersException( std::string( "CSV file reader error: " ) + tableReader.status().ToString() );
}
return *tableReader;
}
void LoadCSVData::ReadArrowTableUsingReader( arrow::csv::TableReader & reader )
{
auto table = reader.Read();
if ( !table.ok() )
{
throw BadParametersException( std::string( "CSV file reader error: " ) + table.status().ToString() );
}
this->mArrowTable = *table;
}
std::unique_ptr<arrow::csv::ParseOptions> LoadCSVData::PrepareParseOptions()
{
auto parseOptions = std::make_unique<arrow::csv::ParseOptions>( arrow::csv::ParseOptions::Defaults() );
parseOptions->delimiter = mDelimiter;
return parseOptions;
}
std::unique_ptr<arrow::csv::ReadOptions> LoadCSVData::PrepareReadOptions()
{
auto readOptions = std::make_unique<arrow::csv::ReadOptions>( arrow::csv::ReadOptions::Defaults() );
readOptions->skip_rows = mNumberOfHeaderRows;
readOptions->block_size = 1 << 27; // 128 MB
readOptions->column_names.reserve( mTable->GetNumberOfColumns() );
for ( auto & colName : mTable->GetColumnsOrder() )
{
readOptions->column_names.emplace_back( colName );
}
return readOptions;
}
std::unique_ptr<arrow::csv::ConvertOptions> LoadCSVData::PrepareConvertOptions() const
{
auto convertOptions = std::make_unique<arrow::csv::ConvertOptions>( arrow::csv::ConvertOptions::Defaults() );
for ( auto & col : mTable->GetColumsInfo() )
{
convertOptions->column_types[col.second.GetName()] = MyTypeToArrowDataType( col.second.GetType() );
}
convertOptions->strings_can_be_null = true;
return convertOptions;
}
std::shared_ptr<arrow::io::ReadableFile> LoadCSVData::OpenCSVFile( const std::string & filePath )
{
MTR_SCOPE_FUNC();
auto inputFileResult = arrow::io::ReadableFile::Open( filePath );
if ( !inputFileResult.ok() )
{
throw BadParametersException( std::string( "CSV file reader error: " ) + inputFileResult.status().ToString() );
}
return *inputFileResult;
}
Maciej, the method TableReader::Read should return shared_ptr<arrow::Table>. The arrow::Table itself has a number of shared pointers to structures which eventually contain the data. To free up the data you will need to make sure that the arrow::Table and any copies of it are destroyed. This should happen as soon as that shared_ptr goes out of scope. However, it appears you are storing the table in a member variable here (which is to be expected, you probably want to use the data after you read it):
this->mArrowTable = *table;
So now you have a second copy of the arrow::Table instance. You could reassign this->mArrowTable to a new blank table or you could destroy whatever this is. Of course, if you are making any other copies of the table then you will need to ensure those go out of scope as well.
Wondering if it's possible to extract the name of a rapidjson::Value directly from it.
For instance, assume we have the following JSON data:
{
"name":
[
{ /*some data*/ },
{ /*some more data*/ }
]
}
And I retrieve the "name" array from it:
rapidjson::Value& myJSONArray = document["name"];
Can I retrieve "name" back from that Value? Something like this:
std::string memberName = myJSONArray.GetMemberName(); // returns "name"
No. It is not possible because an array may not be within an object.
You may use iterator.
Value::MemberIterator itr = document.FindMember("name");
string n = itr->name.GetString();
Value& v = itr->value;
Iterators for object has name and value properties
std::pair<bool, std::string> iterate_items()
{
constexpr std::string_view stringJson = R"([ {"k1": "v1"}, {"k2": "v2"}, {"k3": "v3"}, {"k4": "v4"} ])";
// Wrap input stream for rapidjson reading
rapidjson::MemoryStream memorystreamFile( stringJson.data(), stringJson.length() );
rapidjson::Document documentJson; // Create root rapidjson object
documentJson.ParseStream( memorystreamFile ); // Parse json file
if( documentJson.IsArray() == true ) // Yes, we know it is an array :)
{
for( auto const& it : documentJson.GetArray() ) // iterate array
{
if( it.IsObject() == true ) // They are all objects
{
auto const& _name = it.MemberBegin()->name; // get name
auto const& _value = it.MemberBegin()->value; // get value
std::cout << _name.GetString() << _value.GetString() << "\n"; // dump it
}
}
}
return std::pair<bool, std::string>( true, std::string() );
}
Tutorial with RapidJSON
Well guys/girls, I already asked this but I think I didn't explain good and I couldn't find the solution so I'll ask again with more details and explain more the context of my problem.
I have two classes that contain user data and I want to save them in binary files. On the other hand, I have a template class responsible for save these classes.
There is a really important fact that I have to mention: in the beginning I chose encode a auxiliary class for any class that I would save. This auxiliary class is responsible of writing/reading of data. The original classes have string members and the auxiliary classes have pointers to char. But recently, looking for more simplicity and flexibility, I chose to combine the original class, that contains the benefits of string class ;and its auxiliary that has the pointers that makes the class more comfortable at the moment of save it. So, instead of have two classes, I have one class that handles the input/output of data and the write/read of data.
This change looks something like this:
class AuxNOTE;
//Original Class: Input/Output of Data
class NOTE{
private:
string _Category;
string _Description;
public:
NOTE() : _Category( "" ) , _Description( "" ) { }
NOTE( const NOTE & note ) : _Category( note._Category )
, _Description( note._Description ) { }
NOTE( string category , string description ) : _Category( category)
, _Description( description ) { }
NOTE( const AuxNOTE & aux ) : _Category( aux._Category )
, _Description( aux._Description ) { }
NOTE & operator=( const NOTE & note ){
_Category = note._Category;
_Description = note._Description;
return *this;
}
NOTE & operator=( const AuxNOTE & aux ){
_Category = string( aux._Category );
_Description = string( aux._Description );
return *this;
}
string GetCategory() const { return _Category; }
string GetDescription() const { return _Description; }
void SetCategory( string category ) { _Category = category; }
void SetDescription( string description ) { _Description = description; }
};
//Auxliary Class: Writing/Reading of Data to/from binary files
class AuxNOTE{
private:
char _Category[50];
char _Description[255];
public:
AuxNOTE(){ }
AuxNOTE( const NOTE & note ){
strcpy( _Category , note._Category );
strcpy( _Description , note._Description);
}
AuxNOTE & operator=( const NOTE & note ){
strcpy( _Category , note._Category );
strcpy( _Description , note._Description );
return *this;
}
};
What I have now is something like this:
//Class NOTE: Input/Output of Data and Writing/Reading to/from binary files.
// .h file
class NOTE{
private:
char * _Category;
char * _Description;
public:
NOTE();
NOTE( const NOTE & note );
NOTE( string category , string description );
NOTE & operator=( const NOTE & note )
string GetCategory() const;
string GetDescription() const;
void SetCategory( string category );
void SetDescription( string description );
};
// .cpp file
#include "NOTE.h"
NOTE :: NOTE() : _Category( nullptr ) ,_Description( nullptr )
{
}
NOTE :: NOTE( string description , string category )
: _Category ( new char[ category.size() + 1 ] )
, _Categoria( new char[ description.size() + 1 ] )
{
strcpy( _Categoria , category.c_str() );
strcpy( _Descripcion , description.c_str() );
}
NOTE :: NOTE (const NOTE & copy )
: _Category( nullptr )
, _Description nullptr )
{
if( copy._Description != nullptr ){
_Description = new char[ strlen( copy._Description ) + 1 ];
strcpy( _Description , copy._Description );
}
if( copy._Category != nullptr ){
_Category = new char[ strlen( copy._Category ) + 1 ];
strcpy( _Category , copy._Category );
}
}
NOTE :: ~NOTE() {
if( _Description != nullptr ) delete [] _Description;
if( _Category != nullptr ) delete [] _Category;
}
//Get Methods
string NOTE :: GetDescription() const { return string(_Description); }
string NOTE :: GetCategory() const { return string(_Category); }
//Set Methods
void NOTE :: SetDescription( string description ){
if( _Description != nullptr ) delete [] _Description;
_Description = new char[ description.size() + 1 ];
strcpy( _Description , description.c_str() );
}
void NOTE :: SetCategory( string category ){
if( m_Category != nullptr ) delete [] _Category;
_Category = new char[ category.size() + 1 ];
strcpy( _Category , category.c_str() );
}
//Operators
NOTE & NOTE :: operator=( const NOTE & note ){
if( note._Description != nullptr ) SetDescription( note.GetDescription() );
if( note._Category != nullptr ) SetCategory( note.GetCategory() );
return *this;
}
Note that the public interface looks like if the NOTE class works with string members but it doesn't because it works with pointers to char. Thus the NOTE class can be saved without any problem. However, the class is not responsible at all of writing/reading but I created another class that can save any class as long as these classes have members that can be saved.
And the class that is responsible of this is a template class and looks like this:
template< class T >
class SAVER{
private:
vector< T > _Vector;
string _File;
public:
SAVER( string file );
~SAVER();
};
template< class T >
SAVER< T > :: SAVER( string file ) : _File( file ){
assert( _File != "" );
ifstream file( _File , ios::binary );
if( file.is_open() ){
T obj;
while( file.read( reinterpret_cast<char*>(&obj) , sizeof(obj) ) )
_Vector.push_back( obj );
}
}
template< class T >
Saver< T > :: ~Saver() {
if( _Vector.empty() )
return;
ofstream file( _File , ios::binary | ios::trunc );
assert( file.is_open() );
auto itr = _Vector.begin();
auto end = _Vector.end();
while( itr != end ){
if ( !file.write( reinterpret_cast<char*>( &itr ) , sizeof(itr) ) )
break;
itr++;
}
}
The SAVER's constructor handles the reading and puts the data( e.g NOTE objects ) in its vector. The destroyer handles the writing of all vector's objects into the corresponding binary file.
I got to clear that my errors aren't compile error but they are runtime errors.
Now, This is the problem I have:
When I execute the entire program, it has to read the binary file but it breaks. I open it with the debugger and I see that the program finishes in this line with a "segmentation fault error" and this comes from the SAVER constructor:
NOTE :: ~NOTE() {
if( _Description != nullptr ) delete [] _Description; //It breaks at this line
if( _Category != nullptr ) delete [] _Category;
}
In the debugger I can see the value of _Description and next to it appears an memory error that says: error: Cannot access memory at address (value of _Description).
Why is this happen? Do you see any error? If you need more information or you don't understand something just let me know.
First, search the internet for "c++ serialization library". What you are performing is called serialization.
Pointers and any class that contains pointers cannot be written verbatim to a file. The pointers are locations in memory. There is no guarantee by most Operating Systems that your program will have the exact memory locations next time it is executed. Your program may run in different areas of memory which change where your data is stored.
There are techniques around this, such as either writing the quantity first, then the data or writing the data then some kind of sentinel (such as the '\0' in C-Style strings).
Consider not writing as a binary file, but using formatted textual representations. A number will be read in by many platforms and converted to native representations. A native representation written in binary mode to a file, many not be the same on another platform (look up "Endianess"). Also, most text editors and word processors can easily read text files. Reading and interpreting a binary file is more difficult.
Unless your application's bottleneck is I/O bound and the I/O timing is critical, consider using textual representation of data. It is easier to read (especially when debugging your program) and easily portable.
In the debugger I can see the value of _Description and next to it appears an memory error that says: error: Cannot access memory at address (value of _Description).
Sure, you cannot deserialize pointers from your binary. You need to store their size information and contents in the file instead.
Suppose I have two .dat files; one on my computer and the other one on the other side of the earth - with data constantly being serialized into them through a QDataStream.
The data is parsed the same way – first some sort of ID and then an object associated with that particular ID.
QFile file("data.dat");
QDataStream stream(&file);
file.open("QIODevice::ReadWrite");
stream << *id*; // ID goes in.
stream << *data_object*; // Object with interesting data is serialized into the file.
file.close();
After a while – the first one might look something like this (illustratory, not syntactically correct):
//-------------------------------------DATA.DAT------------------------------------//
ID:873482025
dataObject
ID:129845379
dataObject
ID:836482455
dataObject
ID:224964811
dataObject
ID:625444876
dataObject
ID:215548669
dataObject
//-------------------------------------DATA.DAT------------------------------------//
But the second one hasn't caught up quite yet.
//-------------------------------------DATA.DAT------------------------------------//
ID:873482025
dataObject
ID:129845379
dataObject
ID:836482455
dataObject
//-------------------------------------DATA.DAT------------------------------------//
Is it possible to take both files – detect the differences between them and then "fuse" in the ones that are missing from the second but are present in the first?
Obviously this could be achieved by writing a function extracts the innards of the files, categorizes the contents individually, compares them and so forth – but is there a way to do this by just handling the files themselves, without having to parse the contents individually?
Read both files to extract Id sets.
Read one of the files while appending the objects with missing Ids to the other file.
You can leverage QSet to do set arithmetic. Also, each object would need not only the streaming operators, but also a skipObject static method. I'm also ignoring how you discriminate object types.
typedef qint32_t Id;
bool isOk(const QDataStream & str) { return str.status() == QDataStream::Ok; }
class Object {
...
public:
static void skipObject(QDataStream & str) {
qint8 format;
str >> format;
if (format == 0)
str.skipRawData(32); // e.g. format 0 of this object is 32 bytes long
...
}
};
QPair<QSet<Id>, bool> getIds(const QString & path) {
QSet<Id> ids;
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) return ids;
QDataStream stream(&file);
while (!stream.atEnd()) {
stream >> id;
Object::skipObject(stream);
if (ids.contains(id))
qWarning() << "duplicate id" << id << "in" << path;
ids.insert(id);
}
return qMakePair(ids, isOk(stream));
}
bool copyIds(const QString & src, const QString & dst, const QSet<Id> & ids) {
QFile fSrc(src), fDst(dst);
if (! fSrc.open(QIODevice::ReadOnly)) return false;
if (! fDst.open(QIODevice::WriteOnly | QIODevice::Append)) return false;
QDataStream sSrc(&fSrc), sDst(&fDst);
while (!sSrc.atEnd()) {
Id id;
sSrc >> id;
if (ids.contains(id)) {
Object object;
sSrc >> object;
sDst << id << object;
} else
Object::skipObject(sSrc);
}
return isOk(sSrc) && isOk(sDst);
}
bool copyIds(const QString & src, const QString & dst) {
auto idsSrc = getIds(src);
auto idsDst = getIds(dst);
if (!idsSrc.second || !idsDst.second) return false;
auto ids = idsSrc.first - idsDst.first;
return copyIds(src, dst, ids);
}
I want to convert entity character(Escape character) to HTML in QT, please help me....
i.e: I want to replace " with ", > with >
=====This is my code that not worked====
QString MyApp::ReplaceString(const QString Data, const QString &Before, const QString &After)
{
QString Result = Data;
Result.replace(Before, After, Qt::CaseInsensitive);
return Result;
}
========
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QByteArray data=pReply->readAll();
QString str = codec->toUnicode((const char *)data);
str = Qt::escape(str);
str = ReplaceString(str, """, "\"");
str = ReplaceString(str,">", ">");
I'm not sure I understand what you want, just guessing. You can use QTextDocument. Try something like this:
QTextDocument text;
text.setHtml("<>"");
QString plain = text.toPlainText();
qDebug("%s.", qPrintable(plain));
Remember that QTextDocument needs the gui module.
I think this will solve your problem.
QString escaped=
QString(myhtml).replace("&","&").replace(">",">").replace("<","<");
Test escape() function:
QString plain = "#include <QtCore>"
QString html = Qt::escape(plain);
// html == "#include <QtCore>"
and convertFromPlainText() function:
QString Qt::convertFromPlainText ( const QString & plain, WhiteSpaceMode mode = WhiteSpacePre )
Hello to convert non ASCII character to &#XXX; (where XXX is a number):
/***************************************************************************//*!
* #brief Encode all non ASCII characters into &#...;
* #param[in] src Text to analyze
* #param[in,opt] force Force the characters "list" to be converted.
* #return ASCII text compatible.
*
* #note Original code: http://www.qtforum.org/article/3891/text-encoding.html
*
* #warning Do not forget to use QString::fromUtf8()
*/
QString encodeEntities( const QString& src, const QString& force=QString() )
{
QString tmp(src);
uint len = tmp.length();
uint i = 0;
while( i<len )
{
if( tmp[i].unicode() > 128 || force.contains(tmp[i]) ){
QString rp = "&#"+QString::number(tmp[i].unicode())+";";
tmp.replace(i,1,rp);
len += rp.length()-1;
i += rp.length();
}else{
++i;
}
}
return tmp;
}
/***************************************************************************//*!
* #brief Allows decode &#...; into UNICODE (utf8) character.
* #param[in] src Text to analyze
* #return UNICODE (utf8) text.
*
* #note Do not forget to include QRegExp
*/
QString decodeEntities( const QString& src )
{
QString ret(src);
QRegExp re("&#([0-9]+);");
re.setMinimal(true);
int pos = 0;
while( (pos = re.indexIn(src, pos)) != -1 )
{
ret = ret.replace(re.cap(0), QChar(re.cap(1).toInt(0,10)));
pos += re.matchedLength();
}
return ret;
}
Basic usage:
qDebug() << encodeEntities(QString::fromUtf8("éà#<>hello the world €"),QString("<>"));
// Will print: éà#<>hello the world €
qDebug() << decodeEntities("aßéplopéàçê€");
// Will print: hello world