바이너리 파일을 부호없는 문자 벡터로 읽는 방법
최근에 나는에 이진 파일을 읽는 기능 쓰기 요청을받은 한 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>
.NET을 읽을 때에도 를 사용해야합니다 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
각 요소를 다시 건 드리는 것입니다.
Method 2 and Method 3 look most promising, but could suffer one or more resize
's. Hence the reason to reserve
before reading or inserting.
I would also test with std::copy
:
...
std::vector<byte> vec;
vec.reserve(fileSize);
std::copy(std::istream_iterator<BYTE>(file),
std::istream_iterator<BYTE>(),
std::back_inserter(vec));
In the end, I think the best solution will avoid operator >>
from istream_iterator
(and all the overhead and goodness from operator >>
trying to interpret binary data). But I don't know what to use that allows you to directly copy the data into the vector.
Finally, my testing with binary data is showing ios::binary
is not being honored. Hence the reason for noskipws
from <iomanip>
.
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;
Since you are loading the entire file into memory the most optimal version is to map the file into memory. This is because the kernel loads the file into kernel page cache anyway and by mapping the file you just expose those pages in the cache into your process. Also known as zero-copy.
When you use std::vector<>
it copies the data from the kernel page cache into std::vector<>
which is unnecessary when you just want to read the file.
Also, when passing two input iterators to std::vector<>
it grows its buffer while reading because it does not know the file size. When resizing std::vector<>
to the file size first it needlessly zeroes out its contents because it is going to be overwritten with file data anyway. Both of the methods are sub-optimal in terms of space and time.
I would have thought that the first method, using the size and using stream::read()
would be the most efficient. The "cost" of casting to char *
is most likely zero - casts of this kind simply tell the compiler that "Hey, I know you think this is a different type, but I really want this type here...", and does not add any extra instrucitons - if you wish to confirm this, try reading the file into a char array, and compare the actual assembler code. Aside from a little bit of extra work to figure out the address of the buffer inside the vector, there shouldn't be any difference.
As always, the only way to tell for sure IN YOUR CASE what is the most efficient is to measure it. "Asking on the internet" is not proof.
'Development Tip' 카테고리의 다른 글
Android 메모리 가이드의 '종속성 주입 프레임 워크 방지'가 Dagger에도 적용 되나요? (0) | 2020.12.05 |
---|---|
UITableView 유연 / 동적 heightForRowAtIndexPath (0) | 2020.12.05 |
Haskell에 ML 스타일 모듈을 추가하는 데있어 가장 큰 이론적 어려움은 무엇입니까? (0) | 2020.12.05 |
RequireJS로 지연 로딩을 달성하는 방법은 무엇입니까? (0) | 2020.12.05 |
Boost 속성 트리 write_json이 모든 것을 문자열로 저장하는 이유는 무엇입니까? (0) | 2020.12.05 |