2

Qt ベースではない他のプロジェクトでも使用される一連のソース/ヘッダー ファイルを含む Qt プロジェクトがあります。これらのファイルは、.csv ファイルの読み取りを処理します (CSVReader クラスと呼びます)。CSVReader は、Qt 固有の関数呼び出しなしで記述されています (CSVReader の変更を制御できますが、Qt 固有のコードを使用しないという要件があります)。

私の Qt ベースのアプリケーションでは、これらの余分な .csv ファイルを .qrc ファイルを使用して .exe に埋め込むことで、ユーザーがこれらのファイルを誤って削除したり変更したりできないようにしています。

fopen私のCSVReaderはfread

プロジェクトの Qt 部分で次のようなもの ( QFile を FILE* に変換) を使用して、ファイル ハンドルを 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 ファイルを読んだり開いたりする方法について他の人がアイデアを持っている場合、私は他のアイデアを受け入れます。

ありがとう

4

3 に答える 3

2

は、次の 2 つのCSVReaderいずれかを実行できます。

  1. メモリから解析 - 最初にファイルをメモリマップする必要があります。これは、仮想メモリが不足しない 64 ビット プラットフォームでは理にかなっています。しかし、32 ビット プラットフォームでは、これはあまり柔軟ではありません。サイズが 1 ~ 2 ギガバイトを超えるファイルを開くことはできません。ペアCSVReaderで動作します。const char *, size_t

    メモリ マッピングは、ファイルから明示的に読み取ることと同じではないことに注意してください。ファイルをメモリ マップすると、オペレーティング システムがユーザーに代わって読み取りを行います。ファイルから直接読み取りを行うことはありません。

    ファイルが 32 ビット プラットフォームの仮想メモリに収まるほど小さい場合、または 64 ビット プラットフォームを使用している場合、最新のカーネルのページ マッピング システムが最小のインピーダンス ミスマッチを提供するため、これが最もパフォーマンスの高い方法になります。 IO デバイスとパーサーの間。

  2. 抽象インターフェイスを使用して、ファイルからデータを段階的に読み取ります。はインスタンスCSVReaderで機能しInputInterfaceます。リーダーは、インターフェイスのインスタンスが開いていることを期待する必要があります。

    開くことは特定の実装に固有であるため、リーダーはファイル自体を開くべきではありません。QFileベースの実装はリソース パスを受け入れますが、標準のライブラリ ベースの実装は受け入れないため、ジェネリック メソッドを使用しても意味がありませopenん。それ以外の場合は構造上不可能なエラーが隠されます。

2 番目のアプローチは、適用範囲が広いようです。次のようにインターフェイスを定義できます。

// 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)};
   }
};
于 2016-11-30T19:58:03.190 に答える
0

アプリケーションにメモリ制限がないため、@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());
}
于 2016-11-30T21:00:22.320 に答える
0

Qt QTemporaryFileを利用して、データをコピーして開始することができます。QTemporaryfileすべてのOSで動作します。

次に例を示します (この一時ファイルは全体に関連付けられている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()

于 2016-11-30T22:39:42.870 に答える