제13장 파일처리 스트림의 개념을 이해한다. 객체 지향적인 방법을 사용하여 파일 입출력을 할 수 있다. 텍스트 파일과 이진 파일의 차이점을 이해한다. 순차 파일과 임의 접근 파일의 차이점을 이해한다.
이번 장에서 만들어 볼 프로그램
스트림(stream) 스트림(stream)은 “순서가 있는 데이터의 연속적인 흐름” 이다. 스트림은 입출력을 물의 흐름처럼 간주하는 것이다.
입출력 관련 클래스들
파일쓰기 int main() { ofstream os{ "numbers.txt" }; if (!os) { cerr << "파일 오픈에 실패하였습니다" << endl; exit(1); } for(int i=0;i<100; i++) os << i <<" "; return 0; // 객체 os가 범위를 벗어나면 ofstream 소멸자가 파일을 닫는다.
파일읽기 int main() { ifstream is{ "numbers.txt" }; if (!is) { cerr << "파일 오픈에 실패하였습니다" << endl; exit(1); } int number; while (is) { is >> number; cout << number << " "; cout << endl; return 0; // 객체 is가 범위를 벗어나면 ifstream 소멸자가 파일을 닫는다.
파일모드
예제 int main() { using namespace std; ofstream os("sample.txt", ios::app); if (!os) cerr << "파일 오픈에 실패하였습니다" << endl; exit(1); } os << "추가되는 줄 #1" << endl; os << "추가되는 줄 #2" << endl; return 0;
Lab: 온도데이터를 처리해보자 #1
예제 #include <iostream> #include <fstream>// 파일 입출력을 하려면 헤더 파일을 포함하여야 한다. using namespace std; int main() { ifstream is{ "temp.txt" }; if (!is) { // ! 연산자 오버로딩 cerr << "파일 오픈에 실패하였습니다" << endl; exit(1); } int hour; double temperature; while (is >> hour >> temperature) { cout << hour << "시: 온도 "<< temperature << endl; return 0;
Lab: 온도데이터를 처리해보자 #2 파일에서 읽은 데이터를 객체로 벡터에 저장했다가 다시 꺼내서 화면에 출력해보자.
Solution class TempData { public: int hour; double temperature; }; int main() { ifstream is{ "temp.txt" }; if (!is) { // ! 연산자 오버로딩 cerr << "파일 오픈에 실패하였습니다" << endl; exit(1); } vector<TempData> temps;
Solution int hour; double temperature; while (is >> hour >> temperature) { temps.push_back(TempData{ hour, temperature }); } for ( TempData t : temps) { cout << t.hour << "시: 온도 " << t.temperature << endl; return 0;
멤버함수를 이용한 파일 입출력 get(), put() #include <iostream> #include <fstream> using namespace std; int main() { ifstream is{ "scores.txt" }; if (!is) { // ! 연산자 오버로딩 cerr << "파일 오픈에 실패하였습니다" << endl; exit(1); }
멤버함수를 이용한 파일 입출력 get(), put() char c; is.get(c); // 하나의 문자를 읽는다. while (!is.eof()) // 파일의 끝이 아니면 { cout << c; is.get(c); } cout << endl; return 0;
예제 #include <string> #include <iostream> using namespace std; int main() { string address; cout << "주소를 입력하시오: "; getline(cin, address); cout << "안녕! " << address << "에 사시는 분" << endl; return 0; }
출력 형식 지정
Lab: 줄번호를 붙여보자. 소스가 저장된 텍스트 파일을 읽어서 각 줄의 앞에 숫자 를 붙인 후에 출력 파일에 기록하여 보자.
Solution int main() { ifstream is("scores.txt");; ofstream os("result.txt"); ; if (is.fail()) { cerr << "파일 오픈 실패" << endl; exit(1); } if (os.fail()) {
Solution char c; int line_number = 1; is.get(c); os << line_number << ": "; while (!is.eof()) { os << c; if (c == '\n') { line_number++; } return 0;
텍스트와 이진 파일
텍스트와 이진 파일의 저장 방법 비교
이진 파일 입출력 ofstream os{ "test.dat", ofstream::binary }; int x=5; os.write((char*)&x, sizeof(int)); // 정수 변수는 4바이트로 이루어져 있다.
예제 int main() { int buffer[] = { 10, 20, 30, 40, 50 }; ofstream os{ "test.dat",ofstream::binary }; if (!os) cout << "test.txt 파일을 열 수 없습니다." << endl; exit(1); } os.write((char *)&buffer, sizeof(buffer)); return 0;
예제 int main() { int buffer[5]; ifstream is{ "test.dat", ifstream::binary }; if (!is) cout << "test.txt 파일을 열 수 없습니다." << endl; exit(1); } is.read((char *)&buffer, sizeof(buffer)); for(auto& e: buffer) cout << e << " "; return 0;
Lab: 이진 파일 복사 프로그램 하나의 이미지 파일을 다른 이미지 파일로 복사하는 프로 그램을 작성하여 보자.
Solution #include <iostream> #include <fstream> #include <string> using namespace std; int main() { string ifile, ofile; cout << "원본 파일 이름:"; cin >> ifile; cout << "복사 파일 이름:"; cin >> ofile; ifstream source(ifile, ios::binary); ofstream dest(ofile, ios::binary);
Solution #if 1 dest << source.rdbuf(); #else if (source.is_open() && dest.is_open()) { while (!source.eof()) { dest.put(source.get()); } #endif return 0;
임의 접근 파일 순차 접근(sequential access) 방법: 데이터를 파일의 처 음부터 순차적으로 읽거나 기록하는 방법 임의 접근(random access) 방법: 파일의 어느 위치에서 든지 읽기와 쓰기가 가능한 방법
임의 접근 파일의 원리 파일 위치 표시자: 읽기와 쓰기 동작이 현재 어떤 위치에서 이 루어지는 지를 나타낸다. 강제적으로 파일 위치 표시자를 이동시키면 임의 접근이 가능 파일 위치 표시자
임의 접근 관련 함수 seekg(long offset, seekdir way); is.seekg(ios::beg, 100); is.seekg(ios::end, 0); is.seekg(ios::cur, -100);
예
예제 예제에서는 10개의 난수를 저장한 이진 파일을 만들고 파 일의 크기를 알아낸 다음에 파일의 중간으로 파일 위치 표시자를 이동시켜서 그 위치에 있는 난수를 읽어오는 프 로그램을 작성하여 보자.
Solution const int SIZE = 10; int main() { int data; // 이진 파일을 쓰기 모드로 연다. ofstream os{ "test.dat", ofstream::binary }; if (os.fail()) { cout << "test.dat 파일을 열 수 없습니다." << endl; exit(1); } for (int i = 0; i < SIZE; i++) { data = rand(); cout << data << " "; os.write((char *)&data, sizeof(data)); os.close();
Solution // 이진 파일을 읽기 모드로 연다. ifstream is{ "test.dat", ofstream::binary }; if (is.fail()) { cout << "test.dat 파일을 열 수 없습니다." << endl; exit(1); } // 파일 크기를 알아낸다. is.seekg(0, ios::end); long size = is.tellg(); cout << endl << "파일 크기는 " << size << endl; // 파일의 중앙으로 위치 표시자를 이동시킨다. is.seekg(size/2, ios::beg); is.read((char *)&data, sizeof(int)); cout << "중앙위치의 값은 " << data << endl; return 0;
Lab: 행맨 업그레이드
Solution #include <conio.h> #include <iostream> #include <string> #include <fstream> #include <vector> using namespace std; int main() { vector<string> words; ifstream infile("d:/words.txt"); while (infile) { string word; infile >> word; words.push_back(word); }
Solution while (true) { string r = words[rand() % words.size()]; cout << "이번에 선택된 단어는 " << r << endl; getch(); } return 0;
Lab: 이미지 파일을 읽어서 표시해보자.
Solution #include <windows.h> #include <iostream> #include <fstream> using namespace std; int main() { HDC hdc = GetWindowDC(GetForegroundWindow()); // 이진 파일을 쓰기 모드로 연다. ifstream is{ "d:\\lena(256x256).raw", ifstream::binary }; if (is.fail()) cout << "d:\\lena(256x256).raw 파일을 열 수 없습니다." << endl; exit(1); } int size = 256 * 256; char * memblock = new char[size]; is.read(memblock, size); is.close();
Solution int r, c; for (r = 0; r < 256; r++) { for (c = 0; c < 256; c++) { int red, green, blue; red = green = blue = memblock[r * 256 + c]; SetPixel(hdc, c, r, RGB(red, green, blue)); } delete memblock; return 0;
Q & A