无法为 appending/readwrite 打开 QFile

Cannot open QFile for appending/readwrite

我正在尝试使用以下代码打开现有文件以在其末尾附加数据:

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 中的相同模式。错误代码还是一样。当我从另一个 class 打开文件时,文件打开时没有错误(这不是访问错误)。

可能是什么导致了这个问题?

当您使用 Qt Resource System(qrc 文件)为您的项目添加文件时,它们会直接编译到您的应用程序的二进制文件中,因此是只读的。正如文档所述:-

Resource data can either be compiled into the binary and thus accessed immediately in application code, or a binary resource can be created and at a later point in application code registered with the resource system.

还有...

Currently, Qt always stores the data directly in the executable, even on Windows, macOS, and iOS, where the operating system provides native support for resources. This might change in a future Qt release.

不支持写入资源系统,无论是使用 Qt 的资源系统还是平台原生实现。您的应用程序通常无权修改其自身的可执行文件、应用程序包或其安装位置——如果这样做,将存在安全风险,因为网络代码中的错误很容易被利用来感染用户的系统。所以你想做的只是一个坏主意。

相反,将修改后的资源存储在应用程序的数据文件夹中,如果文件不存在,则恢复为从资源中读取。如果文件很小,追加到文件也可能不是很明智:这样的追加不是原子的,可能会部分失败,从而使文件损坏。使用 QSaveFile 可以保证在不修改任何数据的情况下完全成功或失败。

下面是一个示例实现。 src.close() 不是关闭文件所必需的,因为 QFile 会在销毁时自动关闭,因为它是适当的资源管理 C++ class。通过提前关闭它,我们确保最少使用文件描述符 - 一种有限的系统资源。

// https://github.com/KubaO/Whosebugn/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();
}