最近、バイナリファイルをstd::vector<BYTE>
(BYTE
はunsigned 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
を被る可能性があります。したがって、読み取りまたは挿入の前に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
が尊重されていないことが示されています。したがって、<iomanip>
からのnoskipws
の理由。
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;
ファイル全体をメモリにロードするので、最適なバージョンはファイルをメモリにマップすることです。これは、とにかくカーネルがファイルをカーネルページキャッシュにロードし、ファイルをマッピングすることで、キャッシュ内のこれらのページをプロセスに公開するだけだからです。ゼロコピーとも呼ばれます。
std::vector<>
を使用すると、カーネルページキャッシュからstd::vector<>
にデータがコピーされます。これは、ファイルを読み取りたいだけの場合は不要です。
また、2つの入力反復子をstd::vector<>
に渡すと、ファイルサイズがわからないため、読み取り中にバッファが大きくなります。 std::vector<>
のサイズを最初にファイルサイズに変更すると、ファイルデータで上書きされてしまうため、そのコンテンツが不必要にゼロになります。どちらの方法も、スペースと時間の点で最適ではありません。
サイズを使用してstream::read()
を使用する最初の方法が最も効率的だと思っていたでしょう。 char *
へのキャストの「コスト」はほとんどゼロです-この種のキャストは、コンパイラに「ちょっと、これは別のタイプだと思いますが、このタイプが本当に欲しい...」とコンパイラに伝えるだけです。 、追加のインストゥルメントは追加しません-これを確認する場合は、ファイルをchar配列に読み込んで、実際のアセンブラコードを比較してください。ベクトル内のバッファのアドレスを把握するための少しの余分な作業を除けば、違いはないはずです。
いつものように、あなたのケースで最も効率的なものを確実に伝える唯一の方法は、それを測定することです。 「インターネットで尋ねる」ことは証拠ではありません。