PDA

View Full Version : Reading and rereading a file with QXmlStreamReader



TheRonin
15th May 2009, 14:53
My fellow Qt enthusiasts,

I have a parser class that inherits QXmlStreamReader. The class is supposed to do two things: 1) parse the contents of the file. 2) determine the 'type' of xml file (getType(..) ).

I would like for the getType()-method to read only as far into the file as necessary before returning with the type. That is to say, i want to read as little as possible of the file to determine its type.

The problem is that after returning the type and aborting the read, the subsequent attempt to read the entire file fails with "premature end of document". I have tried using QXmlStreamReader::clear(), i've tried QIODevice::reset(), QIODevice::seek(0), none of them manage to reset the device properly. QIODevice::pos() returns 0 after each of the above operations and yet it does not seem to be properly reset.

If i create two instances of Parser, and let one do getType and the other parse the IODevice, then it works. But it's a cheap victory since it's not behaving the way i want. What am i doing wrong?

Example:

I have two types of xml-files, each identifiable by the root-tag:


<typeA>

...
</typeA>

and


<typeB>

...
</typeB>

My getType() method looks something like this:



FileType Parser::getType(QIODevice* device)
{
setDevice(device);
while (!atEnd()){
readNext();

if (isStartElement()){
if (name() == "typeA"){
return TypeA;
}else if (name() == "typeB"){
return TypeB;
}else{
return UnknownType;
}
}
}

return UnknownType;

}


My read()-method is simple:


bool Parser::read(QIODevice* device)
{
setDevice(device);
while (!atEnd()){
readNext();

cout << "token: " << tokenString().toStdString() << endl;

}
return !error();
}


My calling method looks like this:



QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)){
cout << "error" << endl;
return;
}

Parser reader;
FileType type = reader.getType(&file);
if (type == TypeA){
reader.read(&file);
}

file.close();


As always, thanks in advance!

lni
15th May 2009, 16:43
Not sure if you are supposed to derive a class from QXmlStreamReader. None of the methods in QXmlStreamReader are virtual, not even the destructor...

faldzip
15th May 2009, 18:24
Not sure if you are supposed to derive a class from QXmlStreamReader. None of the methods in QXmlStreamReader are virtual, not even the destructor...

It doesn't matter as typically you are subclassing QXmlStreamReader to make your own reader supposed to read your own specific XML files, ao you add some methods which use QXmlStreamReader's methods.

What happens when you close() and open() file again?
What about saving file data to QByteArray (with readAll()) and use parser on it instead of on file?

I'm just guessing :]

TheRonin
15th May 2009, 20:50
Not sure if you are supposed to derive a class from QXmlStreamReader. None of the methods in QXmlStreamReader are virtual, not even the destructor...

In Java-speak, i'm doing Extends, not Implements. So there needn't be any virtual methods.

faldżip:

Closing and reopening the file had, strangely enough, no effect. I'm thinking it's the StreamReader that doesn't reset-itself properly, even after calling clear() and resetting the device.

I'm sure i could do a readAll as you suggested but my goal was to read as little of the file as possible to save time on I/O and memory. Right now i'm throwing the first reader away and creating a new one when i want to read the entire file, but it doesn't feel like the best solution.

wysota
15th May 2009, 21:07
Why reset the file at all? Can't you continue reading from the place where you stopped?

lni
15th May 2009, 21:42
It doesn't matter as typically you are subclassing QXmlStreamReader to make your own reader supposed to read your own specific XML files, ao you add some methods which use QXmlStreamReader's methods.


What I have done is by following qt designer, by making various Dom objects and let QXmlStreamReader/QXmlStreamWriter read/write the data.

I don't think Qt people would make such mistake by not even providing a virtual destructor if they think people could use it as a based class, of course C++ doesn't stop people from doing it...unlike the keyword "final" in java...

wysota
15th May 2009, 22:00
I don't think Qt people would make such mistake by not even providing a virtual destructor if they think people could use it as a based class, of course C++ doesn't stop people from doing it...unlike the keyword "final" in java...

Correct me if I'm wrong but virtual destructor is needed only if you have some other methods declared as virtual. Lack of virtual methods doesn't prevent you from adding new methods.

lni
15th May 2009, 22:28
Correct me if I'm wrong but virtual destructor is needed only if you have some other methods declared as virtual. Lack of virtual methods doesn't prevent you from adding new methods.

virtual destructor is necessary if the class is intended to be a based class regardless the class itself has virtual methods or not. Otherwise they could be memory leak.

For instance, in the following code, B's dtor will not be called because A's dtor is not virtual. If you make A's dtor virtual, then the memory will be properly freed.

In java, you could add "final" keyword so no one can derive from A, but in C++ there is no such keyword...I guess this is one of C++'s weakness...




#include <iostream>

class A {
public:
~A() {
std::cout << "A::~A called" << std::endl;
}

};

class B : public A {
public:

~B() {
std::cout << "B::~B called" << std::endl;
}

};

int main()
{
A* a = new B;
delete a;

}

wysota
15th May 2009, 23:17
Who said you had to use the class this way? If you cast the object to the base class then you have a point in doing so - you expect some method to be virtual. If you just use the object of the subclass as an extension of the superclass then both destructors will be called. If you have a stack-based object (and we're dealing with one here) then everything will be alright.


int main(){
B b;
return 0;
}

TheRonin
16th May 2009, 03:01
Why reset the file at all? Can't you continue reading from the place where you stopped?

I'm not sure i can since i get the "unexpected end of file"-error when i don't reset. The same is true even if i reset the file or close and reopen it. The only way around this i've found is to create a new streamReader-derived object AND reset the QIODevice:




QFile file(fileName);
file.open(QFile::ReadOnly | QFile::Text);

Parser typeCheck; // type checker!
FileType type = typeCheck.getType(&file);
file.reset();
if (type == TypeA){
Parser reader; // actual reading of the file
if (!reader.read(&file)){
cout << "uh oh." << endl;
}
}else{
cout << "bad file type." << endl;
}
file.close();


Correct me if i'm wrong but this feels like a bug. According to the documentation, QXmlStreamReader::setDevice() is supposed to reset the reader's internal state. If that were true, then doing reset() on the IODevice and resetting the reader's device should work.

wysota
16th May 2009, 23:00
I'm not sure i can since i get the "unexpected end of file"-error when i don't reset.
Because you try reading from the beginning. You have to continue reading where you left off (after determining the internal type of the file).

talk2amulya
17th May 2009, 01:10
In java, you could add "final" keyword so no one can derive from A, but in C++ there is no such keyword...I guess this is one of C++'s weakness...

if you keep a destructor of a class as private, most compilers would not let that class be subclassed..at least thats how Stroustrup intended it, some compilers seem to have ignored that..but all GNU compilers would not let you subclass a class with private destructor..so its no weakness of C++..anything that java can do, c++ can do it on one leg with high heels..with less memory consumption ;)

wysota
17th May 2009, 02:03
anything that java can do, c++ can do it on one leg with high heels..with less memory consumption ;)

Except for proper exception handling :)

lni
17th May 2009, 04:24
if you keep a destructor of a class as private, most compilers would not let that class be subclassed..at least thats how Stroustrup intended it, some compilers seem to have ignored that..but all GNU compilers would not let you subclass a class with private destructor..so its no weakness of C++..anything that java can do, c++ can do it on one leg with high heels..with less memory consumption ;)

How do you delete the object if the dtor is private? You will have to provide a public "release" method to call when you want to delete it, and you won't be able to create an object on stack with private dtor...

For a class without virtual dtor, the object will have no vtable, you won't gain anything to use "IsA" relationship. I would use "hasA" relationship to be more intuitive and avoid possible memory leak. Again, C++ don't stop you from deriving from it....

spam
30th April 2015, 15:04
void QXmlStreamReader::setDevice(QIODevice * device)

Sets the current device to device. Setting the device resets the stream to its initial state.

See also device() and clear().

http://doc.qt.io/qt-4.8/qxmlstreamreader.html#namespaceUri