无法打开QFile以进行追加/读写

时间:2017-03-27 10:45:59

标签: qt qfile

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

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

可能导致此问题的原因是什么?

2 个答案:

答案 0 :(得分:4)

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

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

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

// 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();
}

答案 1 :(得分:3)

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

  

资源数据可以编译成二进制文件,因此可以立即在应用程序代码中访问,也可以创建二进制资源,以及稍后在资源系统注册的应用程序代码中。

和...

  

目前,Qt始终将数据直接存储在可执行文件中,即使在Windows,macOS和iOS上,操作系统也在其中提供对资源的本机支持。这可能会在未来的Qt版本中发生变化。