파일 입출력

pjh5365·2023년 6월 15일
0

[C++]

목록 보기
5/6
post-thumbnail

텍스트 파일 / 바이너리 파일

파일은 저장 매체에 저장된 정보로서 바이트나 블록 단위로 입출력되고 기록되는 데이터 종류에 따라 텍스트 파일과 바이너리 파일로 나뉜다. 텍스트 파일은 오직 문자들만 기록되며 문자 외에 이미지 등 다양한 정보가 기록된다면 바이너리 파일이다.

텍스트 파일

텍스트 파일은 사람들이 사용하는 글자 혹은 문자만으로 구성되는 문서 파일이다. 사람들이 사용하는 글자에는 한글, 영어 알파벳, 숫자, 기호 등 여러가지가 있으며 각 글자마다 고유한 바이너리 코드(이진코드)가 주어진다. 텍스트 파일은 문자들로만 구성되는 파일이므로, 텍스트 파일에는 문자로 해석 가능한 코드 값들만 존재한다.

메모장과 같은 텍스트 편집기들은 사용자가 입력한 Enter 키를 '\r' 과 '\n' 의 두 아스키 코드로 저장하여 코드의 값은 각각 0x0D(13), 0x0A(10) 이다. '\r' 은 캐리지 리턴으로 커서를 현재 줄의 맨 앞으로 옮기도록 하는 것이고, '\n'은 라인피드로 커서를 현재 있는 위치에서 한 줄 밑으로 옮기도록 하는 지시이다. 따라서 다음 줄의 맨 앞으로 넘어가려면 '\r' 과 '\n' 이 모두 필요하다.

바이너리 파일

사진 이미지, 오디오 등의 문자로 표현되지 않는 바이너리 정보를 저장하는 파일이며 바이너리 파일의 각 바이트는 파일을 만든 응용프로그램만이 해석할 수 있다.

파일 입출력

C++ 에서는 ifstream 으로 파일을 읽고, ofstream 으로 파일을 쓴다. fstream 은 하나의 파일에 읽기와 쓰기를 동시에 할 때 사용된다. ifstream과 ofstream 은 파일을 프로그램과 연결하는 스트림으로 ifstream 객체를 통해 파일에서 읽고 ofstream 객체를 통해 파일 쓰기를 진행한다.

파일 입출력을 위하선 <fstream> 헤더 파일과 std의 네임스페이스가 필요하다.

파일 입출력 모드

  • 텍스트 I/O : 문자들만 기록하고, 파일에 있는 바이트를 문자로만 해석하는 입출력 방식으로 텍스트파일을 읽고 쓸때만 사용된다.

  • 바이너리 I/O : 바이트 단위로 바이너리 데이터를 입출력하는 방식으로 모든 파일을 단순히 바이트의 스트림으로 다루기 때문에 텍스트 파일이나 바이너리 파일에 상관없이 읽고 쓰기가 가능하다.

    파일 저장 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
using namespace std;

int main() {
    char name[10], dept[20];
    int sid;

    cout << "이름 >> ";
    cin >> name;
    cout << "학번(숫자) >> ";
    cin >> sid;
    cout << "학과 >> ";
    cin >> dept;

    ofstream fout;  //파일 출력 스트림 생성
    fout.open("student.txt");   //파일을 여는데 파일이 존재하지 않는다면 만들고 존재한다면 내용을 지우고 맨 앞부터 쓰기 시작
    //ofstream fout("student.txt")  //파일 출력 스트림을 생성과 동시에 파일열기
    if(!fout) { //파일 열기에 실패한다면
        cout << "파일을 열 수 없습니다." << endl;
        return 0;
    }

    fout << name << endl;
    fout << sid << endl;
    fout << dept << endl;

    fout.close();   //파일 닫기
}

파일 읽기 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
using namespace std;

int main() {
    char name[10], dept[20];
    int sid;

    ifstream fin;  //파일 입력 스트림 생성
    fin.open("student.txt");   //파일을 열기
    if(!fin) { //파일 열기에 실패한다면
        cout << "파일을 열 수 없습니다." << endl;
        return 0;
    }

    fin >> name;    //파일 읽기
    fin >> sid;
    fin >> dept;

    cout << name << endl;
    cout << sid << endl;
    cout << dept << endl;

    fin.close();   //파일 읽기를 마치고 닫기
}

파일 모드

파일 모드란 파일을 열 때 앞으로 어떤 파일 입출력으로 수행할 것인지 알리는 정보

파일 모드의미
ios::in읽기 위해 파일을 연다.
ios::out쓰기 위해 파일을 연다.
ios::ate(at end) 쓰기 위해 파일을 열며 파일 포인터를 파일 끝에 둔다. 파일 포인터를 옮겨 파일 내의 임의의 위치에 쓸 수 있다.
ios::app파일 쓰기 시에만 적용되며 파일 쓰기 시마다 자동으로 파을 포인터가 파일 끝으로 옮겨져 항상 파일의 끝에 쓰기가 이루어진다.
ios::trunc파일을 열 때 파일이 존재하면 파일의 내용을 전부 지워 크기를 0으로 만든다. ios::out 모드를 지정하면 디폴트로 함께 지정된다.
ios::binary바이너리 I/O로 파일을 연다. 이 파일 모드가 지정되지 않으면 디폴트는 텍스트 I/O 이다.

파일 모드는 파일을 열 때 지정하며, open() 함수나 각 스트림의 생성자를 통해 지정한다. open()의 경우 두번째 매개변수에 파일 모드를 지정한다.

멤버 함수를 이용한 텍스트 I/O

C++ 입출력 클래스에서 파일에는 한 바이트를 읽고 쓰는 get() 과 put() 함수가 있는데 각각 istream과 ostream 클래스의 멤버로 텍스트 파일이나 바이너리 파일을 막론하고 입출력이 가능하다.

get()을 이용한 텍스트 파일 읽기 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
using namespace std;

int main() {
    ifstream fin("student.txt", ios::in);    //디폴트가 ios::in으로 생략이 가능하지만 이렇게 명시적으로 작성도 가능
    if(!fin) {
        cout << "파일 열기 오류" << endl;
        return 0;
    }

    int count = 0;
    int c;
    while((c = fin.get()) != EOF) { //EOF를 만날때 까지 읽어서 c에 입력
        cout << (char)c;
        count++;    //읽은 문자 카운트하기
    }
    
    cout << "읽은 바이트 수  : " << count << endl;
    fin.close();
}

put() 과 ios::app 모드로 텍스트 파일 덧붙여 쓰기 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
using namespace std;

int main() {
    fstream fout("student.txt", ios::out | ios::app);    //쓰기 모드로 파일 열기
    if(!fout) {
        cout << "파일 열기 오류" << endl;
        return 0;
    }

    fstream fin("test.txt");
    if(!fin) {
        cout << "파일 열기 오류" << endl;
        return 0;
    }

    int c;

    while((c = fin.get()) != EOF) {
        fout.put(c);    //c를 하나씩 읽어 다른파일에 하나씩 입력
    }

    fin.close();
    fout.close();
}

텍스트 파일의 라인 단위 읽기

텍스트 파일을 라인 단위로 읽기 위해 아래의 함수를 사용한다.

istream 의 getline(char* line, int n)	//버퍼를 사용해 한 라인을 읽음
getline(ifstream& fin, string& line)	//스트링을 사용해 한 라인을 읽음

getline() 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
#include <vector>
#include <string>
using namespace std;

void fileRead(vector<string> &v, ifstream &fin) {
    string line;
    while(getline(fin, line)) { //getline을 이용해 라인 읽기
        v.push_back(line);
    }
}

void search(vector<string> &v, string word) {
    for(int i = 0; i < v.size(); i++) {
        int index = v[i].find(word);
        if(index != -1) //찾았을 때
            cout << v[i] << endl;
    }
}

int main() {
    vector<string> wordVector;
    ifstream fin("word.txt");
    if(!fin) {
        cout << "파일을 열 수 없습니다." << endl;
        return 0;
    }
    fileRead(wordVector, fin);
    fin.close();

    cout << "파일을 읽었습니다." << endl;
    while(1) {
        cout << "검색할 단어를 입력하세요 >> ";
        string word;
        getline(cin, word);
        if(word == "exit")
            break;
        search(wordVector, word);
    }
    cout << "프로그램을 종료합니다." << endl;
}

바이너리 I/O

바이너리 입출력은 파일의 각 바이트를 바이너리 값 그대로 읽거나, 변수나 버퍼의 바이너리 값을 그대로 파일에 저장하는 저수준 입출력 방식이다. 따라서 텍스트 파일의 경우 바이너리 입출력으로 읽고 쓰기가 되지만 바이너리 파일은 반드시 바이너리 입출력을 사용해야 한다.

get(), put()을 이용한 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
using namespace std;

int main() {
    ifstream fin("image.png", ios::in | ios::binary);    //바이너리 입력스트림 생성
    if(!fin) {
        cout << "파일을 열지 못했습니다." << endl;
        return 0;
    }
    
    ofstream fout("copy.png", ios::out | ios::binary);  //바이너리 출력스트림 생성
    if(!fout) {
        cout << "파일 열기 오류" << endl;
        return 0;
    }

    int c;
    while((c = fin.get()) != EOF) { //파일 끝까지 읽고
        fout.put(c);    //읽은 내용 쓰기
    }
    cout << "복사완료" << endl;

    fin.close();
    fout.close();
}

get()과 put()은 한바이트 단위로 입출력을 수행하지만 read()와 write()는 블록 단위로 입출력하기 때문에 실행 속도가 빠르다.

read()/write() 예시

#include <iostream>
#include <fstream>  //파일 입출력을 위한 헤더파일
using namespace std;

int main() {
    ifstream fin("image.png", ios::in | ios::binary);    //바이너리 입력스트림 생성
    if(!fin) {
        cout << "파일을 열지 못했습니다." << endl;
        return 0;
    }
    
    ofstream fout("copy.png", ios::out | ios::binary);  //바이너리 출력스트림 생성
    if(!fout) {
        cout << "파일 열기 오류" << endl;
        return 0;
    }

    char buf[1024]; //1KB씩 복사
    while(!fin.eof()) {
        fin.read(buf, 1024);    //최대 1KB를 읽어 버퍼에 저장
        int n = fin.gcount();   //실제로 읽은 바이트 수 알아내기
        fout.write(buf, n); //실제로 읽은 바이트 크기만큼 저장하기
    }
    cout << "파일 복사 완료" << endl;
    fin.close();
    fout.close();
}

0개의 댓글