C++ File I/O

이정빈·2023년 6월 1일
0

1. Create, Read, Write

파일은 텍스트 파일 또는 바이너리 파일로 나뉘어져 있다. 구분을 잘해서 각각 맞는 함수를 조절해서 사용할 것

명품 C++ 예제나 문제에 좋은 코드들이 많아서 참고하도록

1.1 File Opening Mode

ios::in 읽기

ios::out 쓰기 ( 이미 있는 파일이면 초기화)

ios::app 추가하기 (존재 x시 생성, 파일의 끝에 추가)

ios::ate 위치를 정할 수 있다. (app와 비슷, 크기 사이즈 알아낼때)

ios::trunc 열때 전부 삭제

ios::binary 이진모드로 열기

1.2 파일 읽기, 쓰기

void main() {
	ofstream fout("tmp.txt", ios::out);
	if (!fout) {
		cout << "no" << endl;
		return;
	}

	int account;
	string name;
	double bal;

	while (cin >> account >> name >> bal) {
		fout << account << name << bal << endl;
	}
}

콘솔창을 통해 입력하면 파일에 적혀진다. 하지만 ios::out이라 그 전에 내용이 있던간에 새로 적혀진다.

void main() {
	ifstream fin("tmp.txt", ios::in);
	if (!fin) {
		cout << "no" << endl;
		return;
	}

	int account;
	string name;
	double bal;

	while (fin >> account >> name >> bal) {
		cout<< account << name << bal << endl;
	}
}

ifstream 사용, 파일에서 읽어내면된다.

1.3 파일 위치 포인터

파일 위치 포인터는 파일 내 현재 위치를 가르킨다. 다음 바이트를 읽거나 쓸 준비를 한다.

  1. seekg() 읽기모드, tellg() 현재 위치
    fin.seekg(10) // 10위치부터 읽음
    fin.seekg(10, ios::beg) // 처음에서 10위치에서
    fin.seekg(10, ios::cur) // 현재에서 10위치에서
    fin.seekg(-1, ios::end) // 끝에서 마지막 문자 위치
fin.seekg(-1,ios::end);
int c=fin.get();

파일의 맨 마지막 문자를 읽을 경우

int c;
while((c=fin.get())!=EOF){
	fin.seekg(9,ios::cur);
}

10바이트 씩 읽기

fin.seekg(0, ios::end);
	int n = fin.tellg();
	for (int i = 0; i < n; i++) {
		fin.seekg(n - 1 - i, ios::beg);
		int c = fin.get();
		cout << (char)c;
	}

거꾸로 출력하기

	fin.seekg(0, ios::end);
	int n = fin.tellg();

파일의 크기 알아내기

(",,,",ios::in|ios::ate);
void main() {
	ifstream fin("test.txt", ios::in);
	if (!fin) {
		cout << "no" << endl;
		return;
	}

	int a;
	string n;
	double b;

	fin.seekg(12);
	while (fin >> a >> n >> b) {
		cout << a << " " << n << " " << b << endl;
		fin.seekg(0, ios::end);
	}

}

2 PBY 50000

텍스트 파일에는 \r\n이 있기 때문에 총 1라인에서는 12바이트를 차지한다.
절대적인 위치를 12로 옮겼으니 다음 줄 부터 시작한다.
그리고 끝으로 옮겼으니 더 이상 읽을게 없으므로 종료한다.

void main() {
	ifstream fin("test.txt", ios::in);
	int a;
	string n;
	double b;

	while (cin >> a >> n >> b) {
		cout << "tellg(): " << fin.tellg() << endl;
		cout << a << ' ' << n << ' ' << b << endl;
		cout << endl;
	}
}

tellg(): 10
1 wook 100

tellg(): 23
2 PBY 50000

tellg(): 40
3 dlwlrma 10000

tellg(): 52
4 IU 70000

한 줄을 다 읽고나서 파일포인터 위치인데 \r\n전이다. 헷갈리지말것
현재의 포지션을 알려주기때문에 첫 문장에서의 \r\n은 포함되지 않고 다음 줄에 포함되었다.

  1. seekp() 쓰기모드, tellp() 출력위치
void main() {
	ofstream fout("test2.txt", ios::out);
	int a;
	string n;
	double b;
	int p = 0;

	cout << "? ";
	while (cin >> a >> n >> b) {
		fout.seekp(p);
		cout << "tellp(): " << fout.tellp() << endl;
		p += 3;
		fout << a << ' ' << n << ' ' << b << endl;
		cout << "\n? ";
	}
}

? 1 wook 100
tellp(): 0

? 2 PBY 50000
tellp(): 3

? 3 dlwlrma 10000
tellp(): 6

? ^Z

1 w2 P3 dlwlrma 10000 //test2

while문을 보자
1. 출력의 절대 위치를 p로 세팅한다.
2. p의 위치를 출력한다.
3. p의 위치를 3씩 증가한다.
4. 파일에 입력한다.

즉, 파일에 위치 3씩 계속 덮히는 구조이다.

void main() {
	ofstream fout("test2.txt", ios::out);
	int a;
	string n;
	double b;

	cout << "? ";
	while (cin >> a >> n >> b) {
		cout << "tellp(): " << fout.tellp() << endl;
		fout << a << ' ' << n << ' ' << b << endl;
		cout << "\n? ";
	}
}

? 1 wook 100
tellp(): 0

? 2 PBY 50000
tellp(): 12

? 3 dlwlrma 10000
tellp(): 25

? ^Z

입력을 하고 tellp에 대해 알려준다. 처음에는 0이 (당연) 나온다.
하지만 첫 줄에는 10문자이지만 \r\n을 포함해 12바이트를 차지한다.
그래서 다음 tellp는 12이다.

2. Sequential Vs Random Access

바이너리 파일로 입출력을 하자는 건데, 뒤에 ios::binary를 꼭 붙여야 한다.

read,write함수가 있는데 이들은 문자열 하나씩 입출력을 한다. 문자열이여야 하며, char형식의 배열이어도 된다. 원하는 size만큼 입출력 가능. 단, n-1, 1개는 종료문자라는 것을 잊지말자.

2.1 get,put()

get과 put으로도 바이너리 바이트를 입출력 가능하다

int c;
	while ((c = fin.get()) != EOF) {
		fout.put(c);
	}

2.2 read, write()

원하는 사이즈만큼 블록단위로 입출력이 가능하다. ( 더 빠름)
read같은 경우 EOF에 도달해야 읽기를 중단하지만, 얼만큼 입력됐는지 알기 어렵다
그래서 fin.gcount()를 사용한다.
읽은 바이트 수를 알아낸다.


char buf[1024];
	while (!fin.eof()) {
		fin.read(buf, 1024);
		int n = fin.gcount();
		fout.write(buf, n);
	}
    

2.3 바이너리 I/O와 텍스트 I/O의 차이점

둘 다 파일의 끝을 인식하는데는 차이가 없다.
결국 \n을 읽고 쓸 때 서로 다르게 작동한다.

임의의 파일에 \n을 저장한다.

fout<< '\n'; // 파일에 \r,\n 두 문자가 기록
fout.put(\n); // 파일에 \r,\n 두 문자가 기록

텍스트 i/o로 읽는 경우

int ch=fin.get(); // 파일에서 \r\n을 읽고, ch에 \n 만 리턴

write도 동일

fout.write(buf,3); // a,b,\r,\n 4바이트

이제는 바이너리 i/o에서 \n을 비교한다.

fout(ios::binary)
fout.write(buf,3) // 파일에 a,b,\n 3바이트 저장

이 코드의 실행 결과 \n만 기록된다.

바이너리에서 \n 을 기록하면 \n만 저장되지만 텍스트는 \r\n이 저장된다.

바이너리 파일에서 \r \n을 읽을 때, 두 문자를 그대로 읽어 오지만, 텍스트의 경우 \n만 리턴한다.

Prac 1

int main() {
	ifstream fin("test2.txt", ios::in);
	char c;
	int cnt = 0;
	int line = 0;
	while (fin.get(c)) {
		cout << c;
		cnt++;
		if (c == '\n') {
			line++;
		}
	}
	cout << "The total number of characters is " << cnt << endl;
	cout << "The number of lines " << line << endl;
	return 0;
}

aaa
bbb
ccc
ddd
This is the test text file.
The total number of characters is 44
The number of lines 5

Prac 1

int main() {
	ifstream fin("test2.txt", ios::in);
	
	int cnt = 0;
	int line = 0;
	string s;
	while (getline(fin,s)) {
		line++;
		cnt += s.size();
		cout << s << endl;
	}
	
	cout << "The total number of characters is " << cnt << endl;
	cout << "The number of lines " << line << endl;
	return 0;
}

aaa
bbb
ccc
ddd
This is the test text file.
The total number of characters is 39
The number of lines 5

Prac 2

int main() {
	ifstream fin1("test.txt", ios::in);
	ifstream fin2("test2.txt", ios::in);
	ofstream fout("test3.txt", ios::out);
	
	char c;
	while (fin1.get(c)) {
		fout << c;
	}

	while (fin2.get(c)) {
		fout << c;
	}

	//int ch;
	//while ((ch = fin1.get()) != EOF) {
	//	fout << (char)ch;
	//}
	//while ((ch = fin2.get()) != EOF) {
	//	fout << (char)ch;
	//}

	fin1.close();
	fin2.close();
	fout.close();
	return 0;
}
int main() {
	ifstream fin1("test.txt", ios::in);
	ifstream fin2("test2.txt", ios::in);
	ofstream fout("test3.txt", ios::out);
	
	string s;
	while (getline(fin1, s)) {
		fout << s << endl;
	}

	while (getline(fin2, s)) {
		fout << s << endl;
	}

	fin1.close();
	fin2.close();
	fout.close();
	return 0;
}

getline은 개행문자 지우는 것을 잊지말자

0개의 댓글