1. ホーム
  2. c++

[解決済み] バイナリファイルを符号なし文字列のベクトルに読み込む方法

2023-03-23 16:55:04

質問

最近、バイナリファイルを読み込んで std::vector<BYTE> ここで BYTEunsigned char . すぐにこんな感じになりました。

#include <fstream>
#include <vector>
typedef unsigned char BYTE;

std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::streampos fileSize;
    std::ifstream file(filename, std::ios::binary);

    // get its size:
    file.seekg(0, std::ios::end);
    fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    // read the data:
    std::vector<BYTE> fileData(fileSize);
    file.read((char*) &fileData[0], fileSize);
    return fileData;
}

への明示的なキャストは不必要に複雑に見えます。 char* を呼び出す際に使わざるを得なかった file.read を呼び出したところで、何の気分転換にもならない。


別の方法として std::istreambuf_iterator :

std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::ifstream file(filename, std::ios::binary);

    // read the data:
    return std::vector<BYTE>((std::istreambuf_iterator<char>(file)),
                              std::istreambuf_iterator<char>());
}

というように、かなりシンプルで短いものですが、やはり std::istreambuf_iterator<char> に読み込んでいるときでも std::vector<unsigned char> .


完全に分かりやすいと思われる最後のオプションは std::basic_ifstream<BYTE> と明示的に表現することです。 入力ファイルストリームが必要で、それを使って BYTE s" :

std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::basic_ifstream<BYTE> file(filename, std::ios::binary);

    // read the data:
    return std::vector<BYTE>((std::istreambuf_iterator<BYTE>(file)),
                              std::istreambuf_iterator<BYTE>());
}

というのがありますが basic_ifstream がこの場合適切な選択かどうかはわかりません。

にバイナリファイルを読み込む最良の方法は何でしょうか? vector ? また、私は何が起こっているのかを知りたいのです。 と、私が遭遇する可能性のある問題は何か(ストリームが正しく開かれないことは別として、これは単純な is_open のチェックで回避できるかもしれません)。

を使用することを好む正当な理由はありますか? std::istreambuf_iterator を使うべき理由はありますか?

(私が見ることができる唯一の利点は、シンプルであることです)

どのように解決するのですか?

パフォーマンスに関するテストを行う場合、私は以下のようなテストケースを含めます。

std::vector<BYTE> readFile(const char* filename)
{
    // open the file:
    std::ifstream file(filename, std::ios::binary);

    // Stop eating new lines in binary mode!!!
    file.unsetf(std::ios::skipws);

    // get its size:
    std::streampos fileSize;

    file.seekg(0, std::ios::end);
    fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    // reserve capacity
    std::vector<BYTE> vec;
    vec.reserve(fileSize);

    // read the data:
    vec.insert(vec.begin(),
               std::istream_iterator<BYTE>(file),
               std::istream_iterator<BYTE>());

    return vec;
}

私の考えでは、方法1のコンストラクタで vector の要素に触れ、その後に read は再び各要素に触れる。

方法 2 と方法 3 は最も有望に見えますが、1 つまたは複数の resize 's. そのため reserve を挿入してください。

私はまた、テストするために std::copy :

...
std::vector<byte> vec;
vec.reserve(fileSize);

std::copy(std::istream_iterator<BYTE>(file),
          std::istream_iterator<BYTE>(),
          std::back_inserter(vec));

結局のところ、最適解は operator >> から istream_iterator (からのオーバーヘッドと良さは、すべて operator >> はバイナリデータを解釈しようとしている)。しかし、データを直接ベクターにコピーできるようなものを使うのはどうなんでしょう。

最後に、バイナリデータを使った私のテストでは ios::binary は尊重されないことがわかりました。そのため noskipws から <iomanip> .