Post

(C++) File IO - 과 pubsetbuf()

ifstream을 사용할지, fopen()을 사용할지는 얻어낸 결과를 어디에 담을 것인지에 따라 결정하면 된다. 어차피 얻어낸 데이터를 가공하는 과정에서 string으로 만들어야 한다거나, string의 메서드를 사용해야 편한 경우라면 그냥 ifstream을 사용한다.

왜냐면, char*에서 string으로 변환하는건 새로운 string을 만들면서 생성자로 char*데이터를 집어넣는 수 밖에 없는데 이 때 Deep copy가 발생 한다. Shallow copy하는 방법 없나 찾아봤는데 아마 없는 것 같다. 그래서 문자열 데이터가 큰 경우 copy시 발생하는 오버헤드를 줄일 필요가 있기 때문에 아예 가공할 데이터의 타입을 따라가는 것이 좋다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <fstream>
#include <iostream>

using namespace std;

int main(int argc, char *argv[]) {
	ifstream ifs;
	char buf[USER_BUFSIZ];
	ifs.rdbuf()->pubsetbuf(buf, USER_BUFSIZ);  // setbuf before open().
	ifs.open("C:\\Users\\umbum\\source\\repos\\a", std::ios::binary);
	if (ifs.fail()) {
		std::cout << "[*] failed to open" << std::endl;
		exit(EXIT_FAILURE);
	}

	filebuf* pbuf = ifs.rdbuf();
	// pbuf->pubsetbuf(buf, BUF_SIZE);  이렇게 해도 되고 
	// ifs.rdbuf()->pub..이렇게 해도 되나 ifs.getline()을 쓸 경우는 이게 낫고, pbuf->sgetn()을 쓸거면 위가 낫다.
	// 이렇게 버퍼를 지정해 놓기만 하면, getline()이든 sgetn()이든 접근 시 알아서 버퍼를 채우고, 버퍼에서 가져오는 듯.
	// 아직 데이터에 접근하지는 않았으니 여기서는 모두 00이 출력된다.
	for (int i = 0; i < BUF_SIZE; i++) {
		printf("%02x ", buf[i]);
	} 
	cout << "\n==============================================" << endl;

	char b4[4];
	pbuf->sgetn(b4, 4);    // 이 때 1. buf에 데이터가 채워지고, 2. b4에 데이터가 copy된다.
	// pbuf->sgetn() 를 반복적으로 호출해주면 offset이 증가하며 알아서 다음 데이터를 가져온다.
	// ifs.getline() 도 버퍼에서 가져오기 때문에 buffer를 사용하기 위해 꼭 sgetn()일 필요는 없다.

	// pbuf->pubseekoff(-4, ifs.cur); 현재 위치 기준 뒤로 감기는 이렇게.

	printf("%02x%02x%02x%02x", b4[0], b4[1], b4[2], b4[3]);  // 데이터 있다.
	for (int i = 0; i < BUF_SIZE; i++) {
		printf("%02x ", buf[i]);
	} // 여기도 채워져 있다.
	ifs.close();
	return 0;
}

pubsetbuf(buf, BUF_SIZE)는 내부적으로 C++의 std::basic_streambuf::setbuf(buf, BUF_SIZE)를 호출하게 되는데, 이는 호출 하나 안하나 버퍼 사이즈에 변화를 주지 않는 C의 setbuf()와는 달리, user-provided buffer로 변경하면서 SIZE도 지정한 대로 변경하게 되므로 최적화에 사용할 수 있다. 따라서 오히려 C의 setvbuf()와 비슷하다.

This post is licensed under CC BY 4.0 by the author.