C++ 프로그래밍, 파일 입출력 ( File Input & Output )
C++ 에서 파일과 관련하여 3개의 스트림 클래스를 지원하고 있다. 이들은 모두 istream, ostream 으로부터 파생된 클래스이며 키보드와 모니터에 입력 기능을 가진 cin, cout 은 각각 istream, ostream 클래스의 객체이다. 아래의 파일을 다루는 입출력 스트림들은 istream, ostream 클래스로부터 파생되었기 때문에 사용하는 방법이 cin, cout 객체와 유사한 점이 많아서 그리 어렵지 않게 익힐 수 있다
- ofstream : 파일에 쓰는 기능의 스트림 클래스
- ifstream : 파일로부터 읽어오는 기능의 스트림 클래스
- fstream : 파일에 대한 읽고 쓰는 기능을 모두 갖춘 스트림 클래스
- wifstream : 유니코드 문자로 쓰여진 텍스트 파일 읽는 스트림
- wofstream : 유니코드 문자를 텍스트 파일에 쓰는 스트림
파일출력스트림(ofstream)을 이용한 기본적인 파일쓰기 예
#include <iostream> #include <locale> #include <fstream> using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; ofstream fout; // 파일출력 스트림 객체 생성 fout.open("C:\\test\\sample.txt"); // 파일 생성 fout << "File writing" << endl; // 파일에 쓰기 fout << "This is file data" << endl; fout << "------------------" << endl; fout.close(); // 리소스 해제 wcout << L"파일에 쓰기 완료" << endl; return 0; }
파일 경로에 한글이 포함된 경우와 파일 내용에 한글을 쓰는 예
#include <iostream> #include <locale> #include <fstream> // ofstream 클래스 #include <cstdlib> // wcstombs() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wofstream fout; // 와이드 문자 파일출력 스트림 객체 생성 wstring wfname(L"C:\\test\\샘플.txt"); // 파일 경로에 한글이 포함됨 // 소스파일의 내용은 UTF-8 유니코드(Wide Character Sets)로 인코딩 // Windows 파일 시스템은 MS949(Multibyte Character Sets)가 사용됨 // 그러므로 소스파일에서 파일시스템에 접근하기 위해서는 Unicode ->MBCS 변환이 필요함 // wcstombs(char* mbcs, wchar_t* wcs, size) 함수가 필요함 char* pfname = new char[wfname.size()*2]; // utf-8은 한글자당 2바이트 wcstombs(pfname, wfname.c_str(), wfname.size()*2); fout.open(pfname); // 파일 생성 fout << "파일에 쓰기 테스트" << endl; // 파일에 쓰기 fout << "이 문장은 파일의 내용입니다" << endl; fout << "------------------" << endl; fout.close(); // 리소스 해제 wcout << L"파일에 쓰기 완료" << endl; return 0; }
파일 열기 모드
- ios::in input
- ios::out output
- ios::ate at the end. 파일 내용의 위치를 가리키는 포인터를 파일의 맨 끝으로 이동한다. 디폴트로 파일의 시작부분
- ios::binary binary
- ios::app 파일 내용 끝에 이어쓰기
- ios::trunc 파일의 기존 내용을 버리고 새로 시작
open() 함수의 두번째 아규먼트로 파일 열기 모드를 설정한 예
ofstream은 열기 디폴트 모드가 ios::out 으로, ifstream은 ios::in 모드로, fstream은 ios::in | ios::out 으로 설정되어 있음. 여러가지 열기 모드가 동시에 필요하다면 OR 연산자( | )로 연결하여 여러개의 모드를 사용할 수 있다
#include <iostream> #include <locale> #include <fstream> // ofstream 클래스 #include <cstdlib> // wcstombs() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wofstream fout; // 와이드 문자열을 파일에 출력하는 스트림 생성 wstring wfname(L"C:\\test\\샘플.txt"); char* pfname = new char[wfname.size()*2]; wcstombs(pfname, wfname.c_str(), wfname.size()*2); fout.open(pfname, ios::out); //ofstream 은 디폴트로 ios::out 모드임 fout << "파일에 쓰기 테스트" << endl; fout << "이 문장은 파일의 내용입니다" << endl; fout << "------------------" << endl; fout.close(); wcout << L"파일에 쓰기 완료" << endl; return 0; }
파일열기에 대한 성공여부를 검사하는 예
// 파일열기 성공여부를 검사하는 예 if(!fout.is_open()) { wcerr << L"파일열기 실패" << endl; exit(1); }
텍스트 파일을 쓰기 / 읽어서 화면에 출력하기
#include <iostream> #include <locale> #include <fstream> // ofstream 클래스 #include <cstdlib> // wcstombs() #include <string> // getline() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wofstream fout; // 와이드 문자를 파일에 출력하는 스트림 생성 wstring wfname(L"C:\\test\\샘플.txt"); char* pfname = new char[wfname.size()*2]; wcstombs(pfname, wfname.c_str(), wfname.size()*2); fout.open(pfname); if(!fout.is_open()) { wcerr << L"파일열기 실패" << endl; exit(1); } fout << L"파일에 쓰기 테스트" << "\n"; fout << L"이 문장은 파일의 내용입니다" << "\n"; fout << "------------------" << "\n"; fout.close(); wcout << L"파일에 와이드 문자열 쓰기 성공" << endl; wifstream fin; fin.open(pfname); // 텍스트 모드로 파일에서 읽기위해 와이드입력 스트림 생성 if(!fin.good()) { wcerr << L"파일 열기 실패" << endl; exit(1); } wstring line; while(!fin.eof()) { getline(fin, line); wcout << line << endl; } fin.close(); wcout << L"파일에서 와이드 문자열 읽기 완료" << endl; return 0; }
바이너리/텍스트 모드로 파일에 출력하는 예제
fout.open(pfname, ios::out|ios::binary);// 바이너리 모드로 파일에 출력할 때 //fout.open(pfname); // 텍스트 모드로 파일에 출력할 때 if(!fout.is_open()) { wcerr << L"파일열기 실패" << endl; exit(1); } fout << "파일에 쓰기 테스트" << "\n"; fout << "이 문장은 파일의 내용입니다" << "\n"; fout << "------------------" << "\n"; fout.close();
위의 코드를 이용하여 바이너리 모드로 파일에 텍스트를 저장한 후 메모장으로 열어본 경우
바이너리 모드를 사용하여 개행문자로 "\n" 을 입력하면 아래 화면처럼 개행문자가 제대로 기능하지 못하는 것을 확인할 수 있다. 윈도우에서는 개행문자가 "\r\n" 인데 위의 코드에서는 "\n" 만 사용했기 때문에 텍스트 파일에 "\n" 만 저장되었고 메모장으로 열어보면 윈도우의 개행문자인 "\n\n" 이 없기 때문에 개행이 되지않고 깨진 문자로 보여지고 한 행에 모두 출력된다
- 텍스트 모드로 파일에 출력할 때는 "\n" -> "\r\n" 으로 변환해준다. 즉, 운영체제에 따른 개행문자를 내부적으로 모든 행마다 추가해준다.
- 텍스트 모드로 파일에서 읽어 올 때는 한 행을 읽어 올 때 각 행의 끝에 있는 개행문자("\n", 혹은 "\r\n")를 제거한 문자열을 가져온다
- 바이너리 모드로 파일에 출력할 때는 주어진 데이터를 변환하지 않고 그대로 파일에 저장한다
- 바이너리 모드로 파일에서 읽어 올 때는 한 행을 읽어서 변환을 거치지 않고 그대로 가져온다
- tellg() : get 포인터의 위치를 pos_type 형으로 리턴한다. pos_type는 정수인데 일반 정수의 범위를 넘을 수 있는 정수이다
- tellp() : put 포인터의 위치를 리턴한다
- seekg() : get 포인터의 위치를 변경한다
- seekp() : put 포인터의 위치를 변경한다
seekg( off_type, direction)
off_type 은 일반 정수의 범위를 넘을 수 있는 정수형이다
direction : ios::beg, ios::cur, ios::end
파일 내의 포인터 이동 예
fin.seekg ( 0, ios::end )
읽기 작업을 하는 파일 내의 맨 끝으로부터 0 만큼 떨어진 곳에 포인터를 위치시킨다
텍스트 파일에서 특정 내용 검색하기 예
- wstring :: find ( L"김인철", 0 ) 와이드 문자열 안의 처음(0)부터 "김인철" 을 찾아서 그 인덱스를 리턴한다
- wstring :: substr ( 3, 3 ) 와이드 문자열 안에서 인덱스 3의 위치부터 3개의 와이드 문자를 읽어온다
다음과 같이 구성된 사용자 정보가 텍스트 파일에 저장되어 있다고 할 대 이름만 읽어와서 화면에 출력하는 기능을 구현해보자
11|김인철|kim@daum.net
12|박기정|park@daum.net
13|최신실|choi@daum.net
14|박지성|jisung@daum.net
15|홍길동|hong@daum.net
#include <iostream> #include <locale> #include <fstream> // wofstream 클래스 #include <cstdlib> // wcstombs() #include <string> // getline() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wstring wfname(L"C:\\test\\users.txt"); char* pfname = new char[wfname.size()*2]; wcstombs(pfname, wfname.c_str(), wfname.size()*2); // 파일에서 와이드 문자열 읽어오기 wifstream fin; fin.open(pfname); // 텍스트 모드로 파일에서 읽기위해 와이드입력 스트림 생성 if(!fin.good()) { wcerr << L"파일 열기 실패" << endl; exit(1); } wstring line; while(!fin.eof()) { // 텍스트 파일에서 한행을 읽어온다 getline(fin, line); // 이름 앞에 있는 첫번째 구분자의 위치(인덱스)를 확인한다 std::string::size_type idx1 = line.find(L"|"); if(idx1 != -1) {// 발견되지 않으면 -1 // 이름 뒤에 있는 구분자의 위치(인덱스)를 확인한다 std::string::size_type idx2 = line.find(L"|",idx1+1); // 이름 앞 뒤에 있는 구분자의 인덱스를 이용하여 이름만 읽어온다 wstring wname = line.substr(idx1+1, idx2-idx1-1); // 파일에서 읽어온 이름을 출력한다 wcout << wname << endl; } } fin.close(); wcout << L"파일에서 와이드 문자열 읽기 완료" << endl; return 0; }
텍스트 파일 복사 (원본파일에서 한행씩 읽어서 사본파일에 저장한다)
#include <iostream> #include <locale> #include <fstream> // wofstream 클래스 #include <cstdlib> // wcstombs() #include <string> // getline() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wstring wfname(L"C:\\test\\users.txt"); char* pfname = new char[wfname.size()*2]; wcstombs(pfname, wfname.c_str(), wfname.size()*2); // 파일에서 와이드 문자열 읽어오기 wifstream fin; fin.open(pfname); if(!fin.good()) { wcerr << L"파일 열기 실패" << endl; exit(1); } // 복사 목적 파일 및 스트림 생성 wstring wfcopy(L"C:\\test\\복사본.txt"); char* pfcopy = new char[wfcopy.size()*2]; wcstombs(pfcopy, wfcopy.c_str(), wfcopy.size()*2);
wofstream fout(pfcopy); if(!fout.good()) { wcerr << "목적파일 스트림 오류" << endl; exit(1); } wstring line; while(!fin.eof()) { // 원본 텍스트 파일에서 한행을 읽어온다 getline(fin, line); // 사본 파일에 복사 저장한다 fout << line << endl; } fin.close(); fout.close(); wcout << L"텍스트 파일 복사하기 완료" << endl; return 0; }
텍스트 파일의 전체 내용을 한번에 목적파일에 복사하는 예
원본파일에서 읽어온 그대로 변환 없이 목적 파일에 복사해야 하므로 파일의 모드를 바이너리(ios::binary)로 설정해야 한다
#include <iostream> #include <locale> #include <fstream> // wofstream 클래스 #include <cstdlib> // wcstombs() #include <string> // getline() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wstring wfname(L"C:\\test\\users.txt"); char* pfname = new char[wfname.size()*2]; wcstombs(pfname, wfname.c_str(), wfname.size()*2); // 원본파일 스트림 wifstream fin; fin.open(pfname, ios::binary|ios::ate); // 포인터를 끝에 위치시킨다 if(!fin.good()) { wcerr << L"파일 열기 실패" << endl; exit(1); } // 파일의 내용 끝에서 원본파일의 전체 크기를 확인한다 int len = fin.tellg(); wchar_t* buf = new wchar_t[len]; // 포인터를 다시 파일 내용의 처음으로 위치시킨다 fin.seekg(0, ios::beg); // 파일 전체 내용을 버퍼에 복사한다 fin.read(buf,len); // 복사 목적 파일 및 스트림 생성 wstring wfcopy(L"C:\\test\\복사본.txt"); char* pfcopy = new char[wfcopy.size()*2]; wcstombs(pfcopy, wfcopy.c_str(), wfcopy.size()*2); wofstream fout(pfcopy,ios::binary); if(!fout.good()) { wcerr << "목적파일 스트림 오류" << endl; exit(1); } // 버퍼에 저장된 원본 파일의 내용을 목적 파일에 저장한다 fout << buf; fin.close(); fout.close(); wcout << L"텍스트 파일 복사하기 완료" << endl; return 0; }
이미지 파일 복사
#include <iostream> #include <locale> #include <fstream> // wofstream 클래스 #include <cstdlib> // wcstombs() #include <string> // getline() using namespace std; int main() { setlocale(LC_ALL, ""); wcout << L"C++ 파일 입출력 스트림" << endl; wstring wfname(L"C:\\test\\sample.png"); char* pfname = new char[wfname.size()*2]; wcstombs(pfname, wfname.c_str(), wfname.size()*2); // 원본파일 스트림 ifstream fin; fin.open(pfname, ios::binary|ios::ate); // 포인터를 끝에 위치시킨다 if(!fin.good()) { wcerr << L"파일 열기 실패" << endl; exit(1); } // 파일의 내용 끝에서 원본파일의 전체 크기를 확인한다 int len = fin.tellg(); cout << "source file size = " << len << endl; //wchar_t* buf = new wchar_t[len]; char* buf = new char[len]; // 포인터를 다시 파일 내용의 처음으로 위치시킨다 fin.seekg(0); // 파일 전체 내용을 버퍼에 복사한다 fin.read(buf,len); // 복사 목적 파일 및 스트림 생성 wstring wfcopy(L"C:\\test\\복사본.png"); char* pfcopy = new char[wfcopy.size()*2]; wcstombs(pfcopy, wfcopy.c_str(), wfcopy.size()*2); ofstream fout(pfcopy,ios::binary); if(!fout.good()) { wcerr << "목적파일 스트림 오류" << endl; exit(1); } // 버퍼에 저장된 원본 파일의 내용을 목적 파일에 저장한다 //fout << buf; // 이 문장 대신 아래처럼 해야 한다 fout.write(buf, len); fout.close(); fin.close(); wcout << L"이미지 파일 복사하기 완료" << endl; return 0; }