Open Qfile for Appending

Open QFile for appending

Open the file in QIODevice::Append mode:

QFile f(...);
if (f.open(QIODevice::WriteOnly | QIODevice::Append)) {
...
}

See also the documentation.

Cannot open QFile for appending/readwrite

There's no support for writing into the resource system, whether implemented using Qt's resource system or native to the platform. Your application typically has no right to modify its own executable, or the application bundle, or its installation location - it'd be a security risk if it did since bugs in networking code could be easily exploited to infect your user's system. So what you're trying to do is just a bad idea.

Instead, store the modified resources in your application's data folder, and revert to reading from the resource if the file doesn't exist. It is also probably not very wise to append to a file if the file is small: such appends are not atomic and can partially fail, leaving the file corrupted. Using a QSaveFile is guaranteed to either completely succeed or to fail without modifying any data.

An example implementation follows. The src.close() is not necessary to close the file, as QFile will automatically close upon destruction, as it is a proper resource-managing C++ class. By closing it earlier we ensure minimal use of the file descriptor - a finite system resource.

// https://github.com/KubaO/stackoverflown/tree/master/questions/resource-bypass-43044268
#include <QtCore>

const char kInsertsFile[] = ":/insertstatements.txt";

QString toWritableName(const QString & qrcFileName) {
Q_ASSERT (qrcFileName.startsWith(":/"));
QFileInfo info(qrcFileName);
return
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ info.path().mid(1) + '/' + info.fileName();
}

QString toReadableName(const QString & qrcFileName) {
Q_ASSERT (qrcFileName.startsWith(":/"));
auto writable = toWritableName(qrcFileName);
return QFileInfo(writable).exists() ? writable : qrcFileName;
}

bool setupWritableFile(QSaveFile & dst, QIODevice::OpenMode mode = {}) {
Q_ASSERT (dst.fileName().startsWith(":/"));
Q_ASSERT (mode == QIODevice::OpenMode{} || mode == QIODevice::Text);
QFile src(toReadableName(dst.fileName()));
dst.setFileName(toWritableName(dst.fileName()));
if (!src.open(QIODevice::ReadOnly | mode))
return false;
auto data = src.readAll();
src.close(); // Don't keep the file descriptor tied up any longer.
QFileInfo dstInfo(dst.fileName());
if (!dstInfo.dir().exists() && !QDir().mkpath(dstInfo.path()))
return false;
if (!dst.open(QIODevice::WriteOnly | mode))
return false;
return dst.write(data) == data.size();
}

bool addInsertToFile(const QString & insert) {
QSaveFile file(kInsertsFile);
if (!setupWritableFile(file, QIODevice::Text))
return false;
if (true) {
// Alternative 1
QTextStream s(&file);
s << insert << '\n';
} else {
// Alternative 2
file.write((insert + '\n').toLocal8Bit());
}
return file.commit();
}

QStringList readInserts() {
QFile file(toReadableName(kInsertsFile));
if (!file.open(QIODevice::ReadOnly))
return {};
return QString::fromLocal8Bit(file.readAll()).split('\n', QString::SkipEmptyParts);
}

int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
app.setApplicationName("resource-bypass-42044268");
qDebug() << "Original Inserts:" << readInserts();
auto rc = addInsertToFile("NewInsert");
qDebug() << "Modification status:" << rc;
qDebug() << "Current Inserts:" << readInserts();
}

QFile opened in QIODevice::Append mode working unexpectedly. Is it a Qt bug?

Just a confusion because of naming of file opening modes. Problem was in setHeader() function.

void StudentTable::setHeader(Header header)
{
QFile file(tableName);
if(!file.exists())
{
qDebug()<<"File "<<tableName<<" doesn't exists";
return;
}
file.open(QIODevice::WriteOnly);
QDataStream stream(&file);

stream<<header;

file.close();
}

Every time I have to write file header I open file in QIODevice::WriteOnly mode; that sounds pretty correct because I am neither reading nor seeking file position. But problem is that QIODevice::WriteOnly mode; clear all the data in file and then writes data to file; What I was expecting to just overwrite;

So conclusion is:

If you have to open a file and overwrite some data;
use ReadWrite mode instead of QIODevice::WriteOnly mode. QIODevice::WriteOnly will clear all previous data before writing

I don't know why they named it like this. But mode naming is really confusing both in qt and C++. Same thing applies to C++ also.

append text into the beginning of a textfile

You can't, see the documentation at http://doc.qt.io/qt-5/qiodevice.html:

QIODevice::Append 0x0004 The device is opened in append mode, so that all data is written to the end of the file.

The problem is even worse, a file is usually stored sequentially on disk, appending (better: inserting) at the start of a file would involve moving all data towards the end of the file, thus a reorganization of filesystem blocks. I'm not sure such a filesystem exists, but if, I guess it would only allow insertion of a multiple of the filesystem block size into a file.

Qt file append open mode

QIODevice::Append | QIODevice::Text should be fine either cases if you don't want to truncate it when it exists.

Overwrite text file vs append

Change

mFile.open(QFile::ReadWrite);

to

mFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text);

The QIODevice vs QFile distinction isn't necessary, but I personally favor using the base class. The Truncate flag will overwrite (i.e., delete) an existing file.

Alternatively, you can follow the other suggestion and open your text file directly using one of QTextStream's constructors. The same QIODevice::OpenMode conventions apply. This only works if mFile is a FILE object and not a QFile, which isn't the case in your example.


A couple additional notes for beginners.

Related Note 1

You didn't ask about this, but I also added the QIODevice::Text flag to ensure that newline characters get translated to/from the local encoding (plain \n vs. \r\n) when you use endl.

A really common mistake is to use \r\n AND QIODevice::Text, which results in text files with double-returns \r\r\n on Windows. Just use QIODevice::Text when opening and simply \n or endl and you'll never have this problem.

Related Note 2

Using QTextStream::endl will automatically call flush() each time. If your loop is large, use "\n" instead to prevent a slowdown unless you actually need to flush every line. The stream will automatically write to disk as its buffer gets full, or when it's closed.

QFile::close() also calls flush(), which makes your mFile.flush() at the end redundant.

QFile: new file name append to tho last saved

You are modifying dir variable with QString::append. Variable dir is obviously a class member of MyClass. Try this instead:

void MyClass::saveSettingsToFile(QString file_name)
{
QString path(dir);
path.append(file_name);
QFile my_file(path);
//...
}

How to write without overwrite in Qfile?

I think you can set flag QFile::Append

QFile inherits QIODevice.

here is doc.QIODevice Class

Can I detect the pre-existance of a QFile after opening it in read/write/append mode?

I must admit that I never tried this personally.

However, I remembered that the usual approach for preventing race conditions in file I/O is to try and handle the possible error case respectively.

Hence, I had a look into QFile::open() what it does offer and found:

QIODevice::NewOnly:

Fail if the file to be opened already exists. Create and open the file only if it does not exist. There is a guarantee from the operating system that you are the only one creating and opening the file. Note that this mode implies WriteOnly, and combining it with ReadWrite is allowed. This flag currently only affects QFile. Other classes might use this flag in the future, but until then using this flag with any classes other than QFile may result in undefined behavior. (since Qt 5.11)


I just realized that (beside of our productive Qt 5.9 installation) I have a newer one which I installed for private fiddling.

Thus, I made a little sample to check this out:

#include <QtWidgets>

int main()
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
for (int i = 1; i <= 2; ++i) {
qDebug() << "Iteration" << i;
QFile qFile("test.txt");
if (!qFile.open(QIODevice::WriteOnly | QIODevice::NewOnly)) {
qDebug() << "qFile.open failed! Error code" << qFile.error();
}
qFile.write("test");
qFile.close();
}
return 0;
}

Output:

Qt Version: 5.11.2
Iteration 1
Iteration 2
qFile.open failed! Error code 5
QIODevice::write (QFile, "test.txt"): device not open

I'm still not quite sure how to find out that it failed exactly due to the already existing file (and not for any other reason). In my case for sure but in general?

(The error code 5 is simply QFileDevice::OpenError.)



Related Topics



Leave a reply



Submit