无法打开 QFile 进行追加/读写
Posted
技术标签:
【中文标题】无法打开 QFile 进行追加/读写【英文标题】:Cannot open QFile for appending/readwrite 【发布时间】:2017-03-27 10:45:58 【问题描述】:我正在尝试使用以下代码打开现有文件以在其末尾附加数据:
void AddPharmacyForm::addInsertToFile(QString insert)
QFile inserts(":/new/prefix1/insertstatements.txt");
if(!inserts.exists())
qDebug() << "File does not exist";
if(inserts.isOpen())
qDebug() << "file is open";
if(inserts.open(QFile::ReadWrite | QFile::Text))
// Another workaround- could not open file with append flag
qDebug() << "im here!";
QString currentInserts;
QTextStream out(&inserts);
out >> currentInserts;
out << endl << insert;
inserts.close();
else
QMessageBox::information(this, tr("Error"), tr("Cannot add new pharmacy! "
"Please contact program designer."
));
qDebug() << "error code: " + QString::number(inserts.error());
return;
此代码的输出是带有错误的 QMessageBox,在 qDebug 中它会生成以下行:
"error code: 5"
它不会通知文件不存在和文件正在打开。我还尝试使用不同的标志打开文件:QFile::ReadWrite、QFile::append、QFile::WriteOnly 以及 QIODevice 中的相同模式。错误代码仍然相同。当我从另一个类打开文件时,文件打开时没有错误(这不是访问错误)。
什么可能导致这个问题?
【问题讨论】:
使用“:/”的意思是要写一个qrc捆绑文件??我认为这根本不可能。 那么qrc中包含的文件是只读的?没有办法在程序上下文中编辑它们? 你指的是预编译的资源文件,只能读取。如果要输出,则需要在磁盘上创建本地副本并将内容写入其中。 【参考方案1】:不支持写入资源系统,无论是使用 Qt 的资源系统实现还是平台原生实现。您的应用程序通常无权修改其自己的可执行文件、应用程序包或其安装位置 - 如果这样做会带来安全风险,因为网络代码中的错误很容易被利用来感染您用户的系统。所以你试图做的只是一个坏主意。
相反,将修改后的资源存储在应用程序的数据文件夹中,如果文件不存在,则恢复为从资源中读取。如果文件很小,附加到文件也可能不是很明智:这样的附加不是原子的,可能会部分失败,从而导致文件损坏。使用QSaveFile
保证完全成功或失败而不修改任何数据。
下面是一个示例实现。 src.close()
不是关闭文件所必需的,因为QFile
会在销毁时自动关闭,因为它是一个适当的资源管理 C++ 类。通过提前关闭它,我们确保了文件描述符的最小使用 - 一个有限的系统资源。
// https://github.com/KubaO/***n/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 appargc, argv;
app.setApplicationName("resource-bypass-42044268");
qDebug() << "Original Inserts:" << readInserts();
auto rc = addInsertToFile("NewInsert");
qDebug() << "Modification status:" << rc;
qDebug() << "Current Inserts:" << readInserts();
【讨论】:
非常感谢。顺便说一句 - 将插入文件存储在 txt 文件中是个好主意吗?我应该考虑使用其他类型的文件还是其他类型的解决方案?也许那样会更容易。 一切都取决于该文件有多大,以及您打算保留数据多长时间。小设置最好使用QSettings
- 它是为它设计的。如果您认为有人会插入包含数兆字节二进制数据的 blob,您可能希望重新考虑您的解决方案。
一般来说,我的 sqlite db 由两个表组成,其中一个包含 png blob 字段。每个表旨在包含数十到数百行。那不是兆字节的二进制数据。根据 Qt 文档,我应该使用组(例如,在我的情况下,“药物”和“药房”)并使用 setValue 方法将每个插入作为值添加。这将创建一组,比如说,200 个插入语句。跨这么大的数据集不是很难吗?
QSettings
应该没问题。您将从内部资源预填充设置,然后仅使用这些设置。【参考方案2】:
当您使用Qt Resource System(qrc 文件)为您的项目添加文件时,它们会直接编译到您的应用程序的二进制文件中,因此是只读的。正如文档所述:-
资源数据既可以编译成二进制文件,然后在应用程序代码中立即访问,也可以创建一个二进制资源,然后在应用程序代码中向资源系统注册。
还有……
目前,Qt 总是将数据直接存储在可执行文件中,即使在 Windows、macOS 和 ios 上,操作系统都提供了对资源的原生支持。这可能会在未来的 Qt 版本中改变。
【讨论】:
请注意,即使是原生资源支持也不会改变任何东西,因为这些资源仍然是只读的!以上是关于无法打开 QFile 进行追加/读写的主要内容,如果未能解决你的问题,请参考以下文章