<개인공부>/[C++]

[C++] 파일입출력(ofstream, ifstream)에 대해서.

BlockDMask 2019. 11. 18. 01:53
반응형

안녕하세요. BlockDMask 입니다.

오늘은 C++ 파일입출력 클래스, 파일에서 부터 문자열을 읽어오고, 문자열을 파일에다 쓰는 클래스에 대해서 알아 볼 것 입니다.

다른 클래스들처럼 멤버함수 쭉 나열하면서 하고 싶은데, 꼭 필요한 함수들만 정리하고 사용법 위주로 한번 글을 작성해보겠습니다.

이 글을 읽으면 이제 파일입출력이 매우 시워질 것입니다.

기본 멤버 함수들을 세세하게 읽는게 귀찮다면 바로 2,3번 예제로 바로 가서 읽어도 바로 코드에 사용할 수 있도록 파일입출력 예제를 쉽게 작성해 놓았습니다.

하.지.만. open, close, getline, write 등등 파일관련 함수의 정확한 작동방법을 알고 사용한다면 더 좋은 프로그래머가 될 수 있겠죠? 

그럼 시작해보겠습니다.


C언어에서 사용하는 fopen, fclose 함수에 대해서 알고싶다면 [바로가기]


<목차>

1. 파일입출력을 위한 기본 지식.

2. C++ 파일입출력 예시, 사용법

3. 제가 프로그램을 만들었을때 사용했던 파일입출력 코드 예시

4. C++ 파일입출력시 주의해야 할 것.


1. C++ 파일입출력을 위한 기본 지식


▼ [C++] 파일읽기 ifstream


ifstream을 풀어쓰면 Input file stream 입니다.

Input = 투입, 입력, 입력장치. 

"프로그램에 파일에 있는 어떠한 것들을 스트림 버퍼에 가지고 와서 프로그램에 입력한다." 이런 풀이가 됩니다.

파일로 부터 어떠한 것들을 가지고와서 프로그램에 입력할 수 있게 도와주는 클래스입니다.

ifstream = file -> program


ifstream 함수 원형과 멤버 변

헤더파일 : <fstream>


1. 파일을 열때 사용하는 open 함수

함수원형 : void open (const char* fileName, ios_base::openmode mode = ios_base::in);

함수원형 : void open (const string& fileName, ios_base::openmode mode = ios_base::in);

함수설명 : 첫번째 인자로 open할 파일 이름이 들어가게 됩니다. 

두번째 인자로는 오픈할 모드인데요. 오픈할 파일을 어떤식으로 사용할지?에 따라 모드를 정하면 됩니다.

ios_base::in - 파일을 read할 목적으로 open할 것이다.

ios_base::out - 파일에 write할 목적으로 open할 것이다.

ios_base::binary - 파일을 바이너리 형태로 open할 것이다.

이외에도 ios_base::ate, app, trunc 의 모드가 있습니다. 

보통의 경우에는 ifstream은 in, ofstream은 out으로 default로 들어가 있기 때문에 따로 넣지 않아도됩니다.


2. 열렸는지 확인하는 is_open 함수

함수원형 : bool is_open() const;

함수설명 : 파일이 열렸는지 확인하는 함수 입니다.


3. 파일을 열었으면 꼭 닫아야죠 close 함수

함수원형 : void close();

함수설명 : 파일과의 연결을 닫아버리는 함수 입니다.


4. char 하나씩 파일에서 프로그램으로 읽어오는 get 함수

함수원형 : istream& get (char& c);

함수설명 : 읽은 파일에서 한char 단위로 일겅서 매개변수로 넣은 c에 넣어주는 함수입니다.

간단 사용법 : 

char c;

while(readFile.get(c))

{

//읽은 char가 c에 들어있습니다.

cout << c;

}


5. char 하나씩 읽으면 감질맛나지, 한줄씩 읽어와볼까? getline 함수

함수원형 : istream& getline(char* str, streamsize len);

함수설명 : 한줄씩 문자열을 읽어서 str에 저장해주는 함수입니다.

한줄의 기준은 '\n' 문자열의 끝을 알리는 개행 문자가 올때 까지, 혹은 파일의 끝을 알리는 EOF를 만날때 까지 입니다


**위 ifstream::getline() 함수를 사용할때 주의할점은 문자열을 받아오는 형태가 char* 타입이기 때문에 string 타입으로 바로 받을 수 없다는 특징이 있습니다.

아 string str 선언해서 str.c_str()을 매개변수로 넣으면 된다구요??

땡입니다. str.c_str()은 string을 const char* 타입으로 변환을 하기 때문에 위 함수 첫번째 매개변수로 사용은 불가능합니다.

그럼 바로 string 타입으로 얻어오는 방법이 없나요? 아닙니다. 있습니다. 그것은 아래 예제에서 확인하시죠.


6. 그래서 파일의 끝이 어디라고? eof 함수

함수원형 : bool eof() const;

함수설명 : 파일의 끝이 나오면 true를 반환하고 아니면 false를 반환합니다.

파일을 읽을때 커서가 움직이게 되는데 그 커서가 getline, get 함수를 돌게되면 쭉쭉쭉 뒤로 가게 됩니다.

eof() 함수가 불리면 커서의 위치를 확인하는 내부 로직에 의해서 파일의 끝에 도착했는지 아닌지를 판단하게 됩니다.

그래서, 파일의 끝을 만나게 되면 true를 반환하게 되는것입니다.


>> 파일 읽기 간단 예제

std::ifstream readFile;             //읽을 목적의 파일 선언

readFile.open("words.txt");    //파일 열기

if(readFile.is_open())    //파일이 열렸는지 확인

{

while(!readFile.eof())    //파일 끝까지 읽었는지 확인

{

char arr[256];

readFromFile.getline(arr, 256);    //한줄씩 읽어오기

}

}

readFile.close();    //파일 닫기


▼ [C++] 파일에 쓰기 ofstream

ofstream을 풀어쓰면 output file stream 입니다.

output = 출력, 생산량

"프로그램에 있는 어떤 것들을 파일에 출력한다." 이런 풀이가 됩니다.

프로그램의 출력을 파일에 할 수 있게 돕는 클래스 입니다.

ofstream = program -> file


ofstream 함수 원형과 멤버 변수

헤더파일 : <fstream>


1. 파일을 열때 사용하는 open 함수

함수원형 : void open (const char* fileName, ios_base::openmode mode = ios_base::out);

함수원형 : void open (const string& fileName, ios_base::openmode mode = ios_base::out);

함수설명 : 첫번째 인자는 파일이름입니다.

두번째 인자는 위에서 파일 읽기에서는 in이었는데 파일에 쓰기는 out으로 매개변수 default가 정해져있는것을 볼 수 있습니다.

사용법은 파일읽기 ifstream에서 사용한 방법과 똑같습니다.

단지 목적이 파일에서 읽어올거냐(in), 파일에 쓸거냐(out)에 따라 두번째 매개변수가 달라지게 됩니다.


2. 파일이 열렸는지 확인하는 is_open 함수

함수원형 : bool is_open() const;

함수설명 : 파일이 열렸는지 확인하는 함수 입니다.


3. 꼬리가 기네요. 문좀 닫읍시다. close 함수

함수원형 : void close();

함수설명 : 파일과의 연결을 닫아버리는 함수 입니다.


4. 파일에 이제 좀 써볼까? write 함수

함수원형 : ostream& write(const char* str, streamsize n);

함수설명 : 첫번째 매개변수로 받은 캐릭터 포인터 타입의 문자열의 n만큼의 길이만큼 파일에 wrtie하는 함수입니다.


>> 파일에 쓰기 간단 예제

std::ofstream writeFile;            //쓸 목적의 파일 선언

writeFile.open("words.txt");    //파일 열기


char arr[11] = "BlockDMask";        //파일에 쓸 문자열


if(writeFile.is_open())    //파일이 열렸는지 확인

{

writeFile.write(arr, 10);    //파일에 문자열 쓰기

}

writeFile.close();    //파일 닫기


**중요합니다. c언어 배열로 나타내는 문자열은 문자열 끝에 '\0'이 들어가 있기 때문에 배열의 "총 길이-1"을 write의 두번째 인자로 넣어야합니다. 

따라서 "BlockDMask\0"는 char[] 배열의 길이는 11이지만, 실제로 문자는 10개 이므로 10을 넣어야 정상적으로 파일에 씌워집니다.

C++ string타입의 문자열로 사용한다면 이런걸 신경쓰지 않아도 되서 매우 편합니다. string의 예제는 아래 예제에서 확인하시죠!


이러한 ifstream, ofstream 클래스를 합쳐서 파일 입출력 클래스라고 말합니다.

앞서 작성해 놓았듯이, 헤더 파일은 <fstream> 파일스트림 입니다.



2. C++ 파일입출력 간단 예시, 사용법


파일 읽기 예제

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
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
 
int main(void)
{
    ifstream readFile;
    readFile.open("test.txt");    //파일 열기
 
    if (readFile.is_open())
    {
        while (!readFile.eof())
        {
            //1. istream의 getline.
            /*
            char tmp[256];
            readFile.getline(tmp, 256);
            cout << tmp << endl;    //지금은 읽은 문자열 바로 출력.
            */
 
            //2. std::getline.
            string str;
            getline(readFile, str);
            cout << str << endl;    //지금은 읽은 문자열 바로 출력.
        }
        readFile.close();    //파일 닫아줍니다.
    }
    return 0;
}
cs


결과.

test.txt에는 아래와 같은 문자열이 있었습니다.

 -> 출력 결과 

 

**istream의 getline도 사용하기 좋지만, std::getline이 string 타입으로 읽을 수 있기 때문에 사용하기 좋습니다.

std::getline은 <string>헤더에 존재합니다.



▼ 파일 쓰기 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
 
int main(void)
{
    ofstream writeFile;
    writeFile.open("test.txt");    //파일 열기(파일이 없으면 만들어짐)
 
    //1. char[] 문자열 쓰기
    char arr[11= "BlockDMask";    //"BlockDMask\0"
    writeFile.write(arr, 10);
 
    //2. string 문자열 쓰기
    string str = " is handsome.";
    writeFile.write(str.c_str(), str.size());
    
    //str.c_str() : C++ string -> const char* 으로 변환해주는 함수
 
    writeFile.close();    //꼭 닫아주기
    return 0;
}
cs


▶ 결과.

test.txt 파일이 만들어지면서, 아래와 같은 문자열이 들어가게 됩니다.


**write로 넘길때는 char[] 배열도 좋지만 사용하기 까다로운점이 있기 때문에 string 타입으로 사용하면 더 좋습니다.



3. 본인이 실제로 만들었던 프로그램에서 사용했던 파일입출력 예시


실제 제가 만든 프로그램은 단어장?을 만드는 프로그램이었습니다.

파일에서 단어들을 읽어화서 화면에 하나씩 랜덤으로 뿌려주는 형식입니다.

처음 프로그램을 만들때는 단어장이 없었기 때문에

프로그램 오픈 -> 단어장(words.txt) 파일이 존재하는지 확인.

있으면? 단어장에서 단어들 다 읽어와서 배열에 저장 후 사용.

없으면? 단어장을 새로 만들고 예제 단어들을 몇개 넣어줌. 그리고 예제 단어들만 이용해서 사용.


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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<string>
#include<fstream>
#include<vector>
using namespace std;
 
int main(void)
{
    //프로그램에 내장되어있는 단어들
    vector<string> words = { "BlockDMask""banana""code""program" };
    int len = static_cast<int>(words.size());
    
    //ifstream readFromFile;
    //readFromFile.open("words.txt");    //이 두줄을 한줄로 해결하는 방법은 아래방법!
    ifstream readFromFile("words.txt");
    
    //맨처음 프로그램을 실행하면 파일이 없는 상태일 겁니다.
    //만약, 파일이 없다면 파일을 만들고 기본문자들 세팅
    if (readFromFile.is_open())    
    {
        //열렸네? 파일이 존재합니다.
 
        words.clear(); //새 단어들을 읽어오기 전에 예제 단어들을 삭제한다.
 
        while (!readFromFile.eof())    //단어장이 끝날때까지.
        {
            //ifstream::getline을 이용해서 char 배열 타입으로 읽어오는 방법은 아래와 같습니다.
            //char arr[256];
            //readFromFile.getline(arr, 256);
 
            //std::getline 함수를 이용해서 string 타입으로 읽어오는 방법.
            //이 방법이 string을 사용하기 더 편하기 때문에 개인적으로 이 방법을 선호합니다.
            string tmp;
            getline(readFromFile, tmp);
            words.push_back(tmp);            //읽어온 단어 저장
        }
        readFromFile.close();    //꼬리 안밟히게 파일 닫아줍니다.
    }
    else
    {
        //파일이 없네? 없으니 새롭게 파일을 만들어주고, 예시 단어들을 넣어두자.
        //is_open이 false 인 경우는 파일이 없거나, 해당 파일에 접근이 불가능한 경우.
        ofstream writeToFile;
        writeToFile.open("words.txt");    //파일을 새로 만들어줍니다.
        for (int i = 0; i < len; ++i)
        {
            string tmp = words[i];
            if (i != len - 1)
            {
                tmp += "\n";    //마지막 단어 빼고 엔터 넣어주기
            }
 
            //tmp.c_str() : C++ string -> const char* 타입으로 변환
            writeToFile.write(tmp.c_str(), tmp.size());    
        }
        writeToFile.close();    //파일 닫기
    }
    return 0;
}
cs


▶맨처음 프로그램을 시작했을 때.

words.txt 파일이 생성되고 예제로 넣어준 단어들이 들어가 있습니다.


▶단어장(words.txt) 파일에 단어들을 넣고 프로그램을 시작했을 때.

아래와 같은 단어장을 넣고 프로그램을 시작하면, words.txt 파일의 단어들을 프로그램이 읽어서

vector words에 아래와 같이 단어긓릏 이쁘게 잘 저장하게 됩니다.

 --------------> 


4. C++ 파일입출력시 주의해야 할 것


▼ 파일에 쓸때. wrtie를 이용할때 char[]을 이용하게 되면 꼭 "배열길이-1" 사이즈만큼 넘겨야합니다.

char[] 배열의 끝에는 '\0' 문자열의 끝을 알리는 문자까지 포함되어있기 때문에 꼭! 꼭! 꼭! 이 문자('\0')를 제외하고 파일에 써야합니다. 다시 예제를 보면

char arr[11] = "BlockDMask";    //"BlockDMask\0".

if(writeFile.is_open())

{

writeFile.write(arr, 10);    //파일에 문자열 쓰기. char[] 타입일땐 꼭 사이즈 확인하기! 꼭 제발...

}


그러니까 그냥 배열길이 신경안쓰게, string 타입으로 넘깁시다!!


string str = "BlockDMask"

if(writeFile.is_open())

{

writeFile.write(str.c_str(), str.size()); //얼마나 간단하게요? C++ string으로 파일입출력 합시다!

}


input file, output file등 파일을 열었으면 꼭! 닫아야합니다.

비유를 해보자면, 우리는 의사입니다. 파일은 환자이고, 우리는 환자를 수술하기 위해서 메스(open)를 이용해서 환자의 몸을 열었습니다. 그러고 안에있는 장기(문자열)의 상태를 보고 치료했습니다. 그러고 나서 의사분들은 봉합을 해주죠?

그렇습니다, 프로그래머인 저희가 해야할 것은 파일을 봉합(close)해 줘야하는 작업입니다. 만약 봉합을 해주지 않는다면..........


꼭 닫으세요 두번 확인하세요.

냉장고 열고 안닫으면 등짝 스매싱.

파일 열고 안닫으면 버그 스매싱.

집문 열고 안닫으면 도난 스매싱.


C++ 파일입출력 이제 자신있게 읽고 쓰고 파일 꼭!닫읍시다. 읽어주셔서 감사합니다.

반응형