У меня есть проект Qt, у которого есть набор исходных / заголовочных файлов, которые также используются в других проектах, которые не основаны на Qt. Эти файлы обрабатывают чтение файлов .csv (я назову это моим классом CSVReader). CSVReader написан без каких-либо вызовов определенных функций Qt (у меня есть контроль над модификацией CSVReader, но с требованием, чтобы он не использовал какой-либо специальный код Qt).
В моем приложении на основе Qt мне нравится, что эти дополнительные файлы .csv встраиваются в .exe с использованием файла .qrc, так что пользователь не может случайно удалить или изменить эти файлы.
Очевидно, что это создает проблему при чтении этих данных, так как мой CSVReader использует такие вызовы, как fopen
а также fread
Я надеялся, что смогу использовать что-то вроде следующего (Конвертировать QFile в FILE *) в части Qt моего проекта и просто передайте дескриптор файла в CSVReader.
QFile myFile("goforward.raw");
myFile.open(QIODevice::ReadOnly);
int fileHandle = myFile.handle();
FILE* fh = fdopen(fileHandle, "rb");
Но, очевидно, поскольку файл существует только в .exe, вызов myFile.handle()
возвращается -1
,
Моя текущая идея (что довольно уродливо) — открыть файл с помощью QFile, затем записать файл на жесткий диск, а затем загрузить файл в CSVReader, используя FILE *f = fopen(fname, "rt");
и после этого я удаляю файл, который я написал.
Если у кого-то есть идеи о том, как читать или же открыть файл qresource Я открыт для других идей.
Спасибо
CSVReader
может сделать одну из двух вещей:
Разбор по памяти — файл должен быть в памятисопоставляются первый. Это имеет смысл на 64-битных платформах, где вам не хватит виртуальной памяти. Но на 32-битных платформах это не очень гибко: вы не сможете открыть ни один файл размером более двух гигабайт. CSVReader
будет работать от const char *, size_t
пара.
Обратите внимание, что отображение памяти не то же самое как явное чтение из файла. Когда вы отображаете файл в память, чтение выполняется операционной системой от вашего имени: вы никогда не выполняете чтение непосредственно из файла самостоятельно.
Если файлы достаточно малы, чтобы поместиться в виртуальную память на 32-битных платформах, или если вы работаете на 64-битной платформе, это, скорее всего, будет наиболее эффективный подход, поскольку система отображения страниц современных ядер будет предлагать наименьшее несоответствие импеданса между устройством ввода-вывода и анализатором.
Читайте данные постепенно из файла, используя абстрактный интерфейс. 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()