90

最近、バイナリ ファイル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>ます。


完全に簡単に見える最後のオプションは、を使用することです。これは、「入力ファイルストリームが必要で、それを使用してsを読み取りたい」というstd::basic_ifstream<BYTE>ことを明示的に表現しています。BYTE

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ますか?
(私が見ることができる唯一の利点は単純さです)

4

5 に答える 5

56

パフォーマンスをテストするときは、次のテスト ケースを含めます。

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 のコンストラクターは 内の要素に触れてからvectorread各要素に再度触れます。

方法 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したがって、 fromの理由は<iomanip>.

于 2014-02-15T20:06:39.160 に答える
26
std::ifstream stream("mona-lisa.raw", std::ios::in | std::ios::binary);
std::vector<uint8_t> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());

for(auto i: contents) {
    int value = i;
    std::cout << "data: " << value << std::endl;
}

std::cout << "file size: " << contents.size() << std::endl;
于 2016-04-16T08:11:03.583 に答える
7

ファイル全体をメモリにロードしているため、最適なバージョンはファイルをメモリにマップすることです。これは、カーネルがとにかくファイルをカーネル ページ キャッシュにロードし、ファイルをマッピングすることによって、キャッシュ内のそれらのページをプロセスに公開するためです。ゼロコピーとも呼ばれます。

使用すると、ファイルを読み取るだけの場合は不要なstd::vector<>カーネルページキャッシュからデータがコピーされます。std::vector<>

また、2 つの入力反復子を渡すstd::vector<>と、ファイル サイズがわからないため、読み取り中にバッファーが大きくなります。最初にファイルサイズにサイズ変更std::vector<>すると、とにかくファイルデータで上書きされるため、その内容を不必要にゼロにします。どちらの方法も、スペースと時間の点で最適ではありません。

于 2013-02-28T15:05:33.777 に答える
3

stream::read()サイズを使用して使用する最初の方法が最も効率的だと思いました。へのキャストの「コスト」は、char *おそらくゼロです。この種のキャストは、コンパイラーに、「ねえ、これが別の型だと思っていることは知っていますが、私は本当にこの型がここに欲しい...」と伝え、追加しません。追加の命令 - これを確認したい場合は、ファイルを char 配列に読み込んで、実際のアセンブラー コードを比較してみてください。ベクター内のバッファーのアドレスを把握するための少しの余分な作業を除けば、違いはないはずです。

いつものように、あなたのケースで何が最も効率的かを確実に伝える唯一の方法は、それを測定することです. 「ネットで聞く」は証拠にはなりません。

于 2013-02-28T15:06:55.970 に答える
0

以下のクラスは、バイナリ ファイルの読み込みと保存で vector を拡張します。私はすでにこの質問に何度も戻ってきたので、これは私の次の返信のためのコードです-そして次にバイナリファイルの保存方法を探している他のすべての人のためのコードです. :)

#include <cinttypes>
#include <fstream>
#include <vector>

class FileVector : public std::vector<uint8_t>
{
    public:

        using std::vector<uint8_t>::vector;

        void loadFromFile(const char *filename)
        {
            std::ifstream file(filename, std::ios::in | std::ios::binary);
            insert(begin(),
                std::istream_iterator<uint8_t>(file),
                std::istream_iterator<uint8_t>());
        }

        void saveTofile(const char *filename) const
        {
            std::ofstream file(filename, std::ios::out | std::ios::binary);
            file.write((const char *) data(), size());
            file.close();
        }
};

注:負荷の最適化については、こちらの他のコメントに記載されているように、ファイル サイズを決定し、必要なスペースを事前に割り当てることを検討してください。

于 2022-01-05T10:52:40.897 に答える