无法打开 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 进行追加/读写的主要内容,如果未能解决你的问题,请参考以下文章

QFile 无法打开大文件

QFile.open 无法打开资源 qrc 中的文件

Qt QFile文件读写

c语言 fopenfwritefclose函数(打开文件进行读写覆盖或追加)

Qt:如何打印 QFile?

Qt每天一例2.在文件末尾追加字符串