PDA

View Full Version : Writing single line inside a text file



Momergil
14th July 2013, 17:20
Hello!

I would like to change a single line inside a QFile (.txt) file, not changing anything else on it.

Now, as this (http://www.qtcentre.org/threads/43984-Start-writing-from-a-specific-line-in-a-file) post tells, the easiest way of doing this is by using memory, and this is what I've implemented by now:



QString textoFinal;
MReadWrite *mrw = new MReadWrite(this);

if (mrw->openRead(filenameNotes))
{
textoFinal.reserve(mrw->size() + 8); //Tomorrow = 8

while (!mrw->isAtEnd())
{
const QString strTemp = mrw->readLine() + "\n";

if (!strTemp.contains(nota->getTitulo()))
textoFinal.append(strTemp);
else
{
if (toWhichTab == TAB_FORTOMORROW)
textoFinal.append("0" + itemSeparator + "Tomorrow" + strTemp.section(itemSeparator,1));
else //if (toWhichTab == TAB_TODAY)
textoFinal.append("0" + itemSeparator + strTemp.section("Tomorrow",1));
}
}

textoFinal.squeeze();

mrw->closeFile();
}
else
FILE_NOT_OPENING;

if (mrw->openWrite(filenameNotes,QFile::Truncate))
{
if (!mrw->write(textoFinal))
BUG_OCCURANCE("changeNoteTodayForTomorrow/write");

mrw->closeFile();
}
else
FILE_NOT_OPENING;

delete mrw;


Note: MReadWrite is a class used to quiclky and easier write and read from text files. It implements QFile and QTextStream.
Note 2: it's irrelevant, but the code above is used to change a specific line in the code putting or taking out a "Tomorrow" string. "itemSeparator" is a default QString used to separate contents in the txt file.
Note 3: Yes, I know, I could be using QStringList, but anyway... :P


The problem, thought, is that this solution is quite bad when we are using huge text files. E.g., imagine I want to change the first line in a text file with 10.000 lines, each of them quite long. In that case, even with reserve/squeeze, the idea of huge memory comsumption in order to change one miserable single line seems quite inappropriate. Not to mention that reading from and writing to the hard disk is usually not understood as fast actions.

So my question is: is there a way to change a single line in file without having to appeal to reading the entire file into memory, changing the desired line and writing it all back to the file?

I already tried a solution such as:



bool MReadWrite::writeLine(const QString text)
{
/*!
* \abstract This function is intended to write only a line
* inside the file, making all the rest of the
* file intact.
*
* \pre *goToLine already used
*/

if (!isReading() || !isWriting())
return false;

const qint64 currPosition = getPosition();
const int lineSize = getLineExtension();

QString strBlanck;

if (lineSize > 5)
strBlanck.reserve(lineSize);

for (int aaa = 0; aaa < lineSize; aaa++)
strBlanck.append(" ");

if (!write(strBlanck))
return false;

goToPosition(currPosition);

return write(text);
}


But this function had quite an error. To explain, it opens the file as a ReadWrite mode, pick the current QTextStream position, write on it a fully blanck line, return to the original position and then write the intendend text. Now this function worked very well if the new string (const QString text) is shorter than the original one; but if is size is bigger, than it causes a error of "eating" the next line:



//original text file data
1234567890
1234567890
1234567890
1234567890

//text file data with shorter new string
XXXX
1234567890
1234567890
1234567890

//text file data with larger new string
XXXXXXXXXXXX
34567890
1234567890
1234567890


And I have no idea about how to fix this.

Glad for any help!!



Obs.: btw, if you know a better way of creating a blanck QString without having to use a for with append(" "), I'ld be glad to know :)

ChrisW67
15th July 2013, 00:39
You can only randomly update the file if the space already exists for the data you are inserting. If the file size needs to change (longer or shorter) then there is no way to avoid rewriting the entire file.


if you know a better way of creating a blanck QString without having to use a for with append(" "), I'ld be glad to know


QString blanks(80, ' ');
QString hyphens(80, '-');

Momergil
15th July 2013, 04:36
You can only randomly update the file if the space already exists for the data you are inserting. If the file size needs to change (longer or shorter) then there is no way to avoid rewriting the entire file.

Sad :/ So this essentially means that if I open a .txt file say in Notepad.exe and that is a large file (10.000 lines or so) and goes there and edit one single line, making it bigger, than Notepad.exe actually rewrites the entire file? \o/ Same with a .docx file? (which I supposed would be treated in Qt with QTextDocument, which I imagine to be a little different from the treatment with QFile/QTextStream.)

Thanks for the code!

ChrisW67
16th July 2013, 02:22
Yes, entire files are rewritten when you update a sequential file. The Microsoft .docx file is a zipped XML file, i.e. effectively text, and operates the same way.

By treating a text file as a random-access binary file you can perform some limited in-place updates of mid-file content but nothing that changes the size of the portion you are replacing. The Qt binary installer does something like this to update the various Qt install paths embedded in the Qt libraries: corelib/global/qconfig.cpp holds a preallocated, fixed-size buffer for each path and the installer writes the actual installed path into that space in the QtCore library binary file without rewriting the entire file.