i有一个QT项目,该项目具有一组源/标头文件,这些文件也用于其他不是基于QT的项目中。这些文件处理.CSV文件的读取(我将其称为我的CSVReader类)。CSVReader是编写的,没有任何QT特定函数调用(我对CSVReader的修改有控制权,但是要求它不使用任何QT特定代码)。
在我的基于QT的应用程序中,我喜欢这些额外的.csv文件,将用户无法意外删除或修改这些文件。
显然,在阅读此数据时,这会引起问题,因为我的CSVReader使用fopen
和fread
我希望我可以在项目的QT部分中使用以下内容(将qfile转换为文件*),然后将文件句柄传递给CSVReader。
QFile myFile("goforward.raw");
myFile.open(QIODevice::ReadOnly);
int fileHandle = myFile.handle();
FILE* fh = fdopen(fileHandle, "rb");
,但显然,由于该文件仅存在于.exe中的myFile.handle()
返回-1
。
我当前的想法(这很丑陋)是使用qfile打开文件,然后将文件写入硬盘驱动器,然后使用FILE *f = fopen(fname, "rt");
加载文件,然后将文件加载到CSVReader中,然后删除我编写的文件。
如果其他人对如何读取或 open 我向其他想法打开的qresource文件。
谢谢
CSVReader
可以做两件事:
-
从内存中解析 - 文件必须是内存 - 映射。在64位平台上,这是有意义的,在该平台上您不会用完虚拟内存。但是在32位平台上,这不是很灵活:您将无法在一两个千兆字节上打开任何文件。
CSVReader
将从const char *, size_t
对工作。请注意,内存映射与从文件明确读取的不同。当您的内存映射文件时,读取是由操作系统代表您完成的:您永远不会直接从文件中读取任何内容。
如果文件足够小,可以适合32位平台上的虚拟内存,或者您在64位平台上,这很可能是最佳性能的方法,因为现代内核的页面映射系统将提供IO设备和解析器之间的阻抗不匹配。
-
使用抽象接口从文件逐渐读取数据。
CSVReader
将在InputInterface
实例上使用。读者应该期望接口的开放实例。读者不应打开文件本身,因为开放是特定实现的特定的。由于基于
QFile
的实现将接受资源路径,而基于标准库的实现将接受通用的open
方法:它将隐藏错误的错误。
第二种方法似乎具有最广泛的适用性。您可以按以下方式定义接口:
// https://github.com/KubaO/stackoverflown/tree/master/questions/file-interface-40895489
#include <cstdint>
class InputInterface {
protected:
InputInterface() {}
public:
virtual int64_t read(char *, int64_t maxSize) = 0;
virtual int64_t pos() const = 0;
virtual bool seek(int64_t) = 0;
virtual bool isOpen() const = 0;
virtual bool atEnd() const = 0;
virtual bool ok() const = 0;
virtual bool flush() = 0;
virtual void close() = 0;
virtual ~InputInterface() {}
};
基于QFile
的实现可能看起来如下:
#include <QtCore>
class QtFile : public InputInterface {
QFile f;
public:
QtFile() {}
QtFile(const QString &name) : f(name) {}
bool open(const QString &name, QFile::OpenMode mode) {
close();
f.setFileName(name);
return f.open(mode);
}
bool open(QFile::OpenMode mode) {
close();
return f.open(mode);
}
void close() override {
f.close();
}
bool flush() override {
return f.flush();
}
int64_t read(char * buf, int64_t maxSize) override {
return f.read(buf, maxSize);
}
int64_t pos() const override {
return f.pos();
}
bool seek(int64_t pos) override {
return f.seek(pos);
}
bool isOpen() const override {
return f.isOpen();
}
bool atEnd() const override {
return f.atEnd();
}
bool ok() const override {
return f.isOpen() && f.error() == QFile::NoError;
}
QString statusString() const {
return f.errorString();
}
};
普通的C 实现将是:
#include <cstdio>
#include <cerrno>
#include <cassert>
#include <string>
class CFile : public InputInterface {
FILE *f = nullptr;
int mutable m_status = 0;
public:
CFile() {}
CFile(FILE * f) : f(f) {
assert(!ferror(f)); // it is impossible to retrieve the error at this point
m_status = 0;
}
~CFile() { close(); }
void close() override {
if (f) fclose(f);
f = nullptr;
m_status = 0;
}
bool open(const char *name, const char *mode) {
close();
f = fopen(name, mode);
if (!f) m_status = errno;
return f;
}
bool flush() override {
auto rc = fflush(f);
if (rc) m_status = errno;
return !rc;
}
bool isOpen() const override { return f; }
bool atEnd() const override { return f && feof(f); }
bool ok() const override { return f && !m_status; }
int64_t read(char * buf, int64_t maxSize) override {
auto n = fread(buf, 1, maxSize, f);
if (ferror(f)) m_status = errno;
return n;
}
bool seek(int64_t pos) override {
auto rc = fseek(f, pos, SEEK_SET);
if (rc) m_status = errno;
return !rc;
}
int64_t pos() const override {
if (!f) return 0;
auto p = ftell(f);
if (p == EOF) {
m_status = errno;
return 0;
}
return p;
}
std::string statusString() const {
return {strerror(m_status)};
}
};
我选择采用 @kuba的第一种方法,因为我的应用程序中没有内存限制。对于那些有兴趣的人,我在下面发布了我的普通C 和基于QT的方法。
普通C
std::ifstream ifs("file.csv");
std::string fileContents((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
CSVReader csvReader(fileContents);
基于QT的
QFile qF(":/file.csv");
if (qF.open(QFile::ReadOnly))
{
QTextStream qTS(&qF);
CSVReader csvReader(qTS.readAll().toStdString());
}
您可以利用qt qtemporaryfile复制数据并开始。QTemporaryfile
在每个操作系统上都可以使用。
这是一个示例(此临时文件与整个qApp
关联,因此一旦您退出应用程序,它将被删除):
QTemporaryFile tmpFile(qApp);
tmpFile.setFileTemplate("XXXXXX.csv");
if (tmpFile.open()) {
QString tmp_filename=tmpFile.fileName();
qDebug() << "temporary" << tmp_filename;
QFile file(":/file.csv");
if (file.open(QIODevice::ReadOnly)) {
tmpFile.write(file.readAll());
}
tmpFile.close();
}
您可以重新打开文件tmpFile.fileName()