일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- jupyter
- c# winform
- 구조체
- Unity
- 유니티
- C# delegate
- Python
- 포인터
- vim
- jupyter lab
- c# 추상 클래스
- Algorithm
- HTML
- docker
- Houdini
- 다트 언어
- c# 윈폼
- c언어
- git
- github
- C++
- 플러터
- C언어 포인터
- c#
- dart 언어
- Data Structure
- Flutter
- 깃
- gitlab
- 도커
- Today
- Total
nomad-programmer
[Programming/C++] C++ 스타일의 입출력 본문
// 예를 들어 int타입의 최대 값을 구하고 싶다면 <limits> 헤더 파일을 포함시킨 후
// numeric_limits<int>::max() 처럼 호출하면 된다.
#include <iostream>
#include <limits>
#include <iomanip>
using namespace std;
template<typename T>
void ShowRange(const char* typeName) {
cout << setw(20) << right << typeName << " (" << sizeof(T) << ") = "
<< setw(15) << right << numeric_limits<T>::min() << " ~ "
<< setw(15) << left << numeric_limits<T>::max() << endl;
}
int main() {
ShowRange<signed char>("signed char");
ShowRange<unsigned char>("unsigned char");
ShowRange<signed short>("signed short");
ShowRange<unsigned short>("unsigned short");
ShowRange<signed int>("signed int");
ShowRange<unsigned int>("unsigned int");
ShowRange<signed long>("signed long");
ShowRange<unsigned long>("unsigned long");
ShowRange<float>("float");
ShowRange<double>("double");
return 0;
}
// 결과
signed char (1) = ~
unsigned char (1) = ~
signed short (2) = -32768 ~ 32767
unsigned short (2) = 0 ~ 65535
signed int (4) = -2147483648 ~ 2147483647
unsigned int (4) = 0 ~ 4294967295
signed long (4) = -2147483648 ~ 2147483647
unsigned long (4) = 0 ~ 4294967295
float (4) = 1.17549e-38 ~ 3.40282e+38
double (8) = 2.22507e-308 ~ 1.79769e+308
iostream 헤더 파일에 보면 다음과 같이 cout과 cin 객체를 선언하고 있다. extern이 붙어있는걸 봐서는 실체 객체의 정의는 어딘가 구현 파일에 있을 것으로 예상할 수 있다.
- extern ostream cout;
- extern istream cin;
중요한 것은 ostream과 istream 클래스 타입을 갖는다는 점인데, 사실 이들은 그냥 클래스가 아니라 템플릿 클래스를 다음과 같이 재정의한 타입이다.
- typedef basic_ostream<char> ostream;
- typedef basic_istream<char> istream;
결국 cout과 cin객체의 실제 클래스는 basic_ostream과 basic_istream인 것이다. 클래스의 이름을 보면 기본적인(basic) 출력(output) 스트림(stream), 기본적인 입력(input) 스트림 정도로 해석이 되는데, 여기서 스트림의 개념을 이해할 필요가 있다.
스트림이란 시내, 개울, 흐름 등의 사전적인 의미가 있다. 그와 비슷하게 C++에서의 스트림은 연결된 통로로 흘러가고 있는 데이터를 연상하면 된다.
위의 그림에서 볼 수 있듯이 스트림의 왼쪽에서 데이터를 넣으면 스트림의 오른쪽에 있는 프로그램에서 데이터를 꺼내서 사용한다. 그것이 실제로 누구로부터 왔는지는 신경 쓰지 않아도 된다. 파일일 수도 있고, 콘솔일 수도 있고, 또 다른 프로그램일 수도 있다. 결국 스트림의 양 끝에서 데이터를 주고 받는 객체가 서로에 대해서 모르는 상태에서도 데이터를 주고 받을 수 있다. 즉, 스트림을 통해서 서로간의 연관성(Coupling)이 없어지는 것이다.
제일 아래쪽에 basic_istream과 basic_ostream 클래스가 있고, 이 두 클래스 모두 basic_ios 클래스를 상속 받고 있다. basic_ios는 입출력 스트림 클래스들에서 공통적으로 사용하는 기본 기능을 구현하고 있다. 또한 스트림 내부적으로 사용하는 메모리를 관리하기 위한 basic_streambuf 클래스도 있다. 그리고 그 위에는 ios_base라는 최상위 클래스가 있는데, 이 클래스는 출력을 예쁘게 만들 수 있는 여러 가지 옵션을 구현하고 있다.
버퍼링과 방출
출력 스트림을 사용해서 자료를 보낼 때, 자료가 파일이나 콘솔 창으로 그 즉시 출력되는 것이 아니다. 우선은 받은 자료를 모아두었다가 적당한 양이 쌓이면 한 번에 콘솔 창이나 파일로 출력하게 된다. 매번 보내는 것 보다 한 번에 모아서 보내는 것이 보다 효율적이기 때문인데, 특히 파일에 자료를 출력하는 경우에는 더욱 그렇다.
입력 스트림을 사용하는 경우에도 마찬가지로 파일이나 콘솔 창에서 한 번에 여러 바이트를 모아서 읽어온 후에 조금씩 주는 방식을 사용한다. 이렇게 데이터를 모았다가 보내는 작업을 버퍼링(Buffering)이라고 부른다.
일반적으로 자료가 어느 한 곳에서 다른 곳으로 이동할 때 잠시 보관되는 메모리를 버퍼라고 부르고, 버퍼를 사용해서 모았다가 한 번에 보내는 것을 버퍼링이라고 부른다. C++의 입출력에서만 사용하는 용어는 아니고 컴퓨팅 전반에 걸쳐서 사용하는 용어다.
그런데 가끔씩은 버퍼링을 하지 않게 만들고 싶은 경우가 있다. 예를 들어 출력한 내용을 곧바로 다른 작업에 사용해야 한다면 될 수 있는 한 빨리 출력할 필요가 있다. 이럴 때는 버퍼에 있는 내용을 지금 당장 방출(Flushing)하라는 명령을 내릴 수가 있는데, 다음과 같은 두 가지 방법을 사용할 수 있다.
cout << "Hello World!" << endl << flush;
cout << "Hello World!" << endl;
두 줄의 코드는 동일한 일을 하고 있는데, 첫번째 줄은 Hello, World 라는 문자열을 버퍼로 보낸 후에 버퍼의 내용을 그 즉시 출력하게 만든다. 두 번째 줄에서 쓰인 endl은 개행 문자를 출력한 후에 버퍼의 내용을 방출하게 만든다.
일반적으로 방출 명령을 사용할 일은 거의 없다. 그 대신 내부적으로 버퍼링을 수행한다는 사실을 꼭 기억하고 있어야 한다. 혹시라도 출력한 문자가 바로 출력되지 않는 문제를 만나게 되면 버퍼링을 의심해볼 수 있어야 한다.
입출력 형식 지정
입출력 형식을 지정한다는 것은 한 마디로 출력을 예쁘게 꾸미는 것을 말한다. 출력하는 값을 한 쪽으로 정렬한다던가 16진수로 출력하게 만든다거나, 소수점 이하 자리 수를 제한하는 등의 작업을 말하는 것이다.
setf() 함수
입출력 형식을 지정하는 가장 기본적인 방법은 iso_base 클래스의 setf() 함수를 사용하는 것이다. setf() 함수는 다음과 같이 오버로드된 두 가지 버전이 있다.
fmtflags setf(fmtflags f);
fmtflags setf(fmtflags f, fmtflags mask);
예를 들어 bool 값을 0, 1이 아닌 true, false의 형태로 출력하고 싶다면 다음과 같이 하면 된다.
cout.setf(ios_base::boolalpha);
cout << true << endl; // 1이 아닌 true가 출력된다.
setf() 함수의 인자로 넣을 수 있는 값들은 다음에 나오는 표에 정리해두었다. 이 값들은 iso_base 클래스 안에서 정의하고 있기 때문에 iso_base::boolalpha처럼 영역을 지정해서 사용해야 한다.
구분 | 값 | 의미 |
인자가 하나인 setf() 함수에 사용할 수 있는 값 | showbase | 진법을 나타내는 기호(0x, O)를 함께 출력한다. 예를 들어 16진수는 0x1F처럼 출력한다. |
showpoint | 실수를 출력할 때 항상 소수점을 출력한다. | |
showpos | 양수의 겨우에도 + 기호를 붙여서 출력한다. | |
uppercase | 실수를 scientic으로 출력할 때 사용하는 문자 'E'나 정수를 16진수로 출력할 때 사용하는 영문자들을 대문자로 출력한다. | |
boolalpha | bool 타입의 값을 1, 0이 아닌 true, false의 형태로 출력하거나 입력 받는다. | |
인자가 두 개인 setf() 함수의 첫번째 인자로 사용할 수 있는 값 | dec | 정수를 10진수로 출력하거나 입력 받는다. |
hex | 정수를 16진수로 출력하거나 입력 받는다. | |
oct | 정수를 8진수로 출력하거나 입력 받는다. | |
internal | 값을 오른쪽으로 정렬해서 출력한다. 하지만 부호(+,-) 혹은 진법을 나타내는 기호(0x, 0)는 왼쪽으로 정렬해서 출력한다. | |
left | 값을 왼쪽으로 정렬해서 출력한다. | |
right | 값을 오른쪽으로 정렬해서 출력한다. | |
fixed | 실수를 항상 123.45와 같은 방식으로 출력한다. fixed로 지정하지 않는 경우에도 123.45와 같이 출력하지만 실수의 값이 너무 작거나 너무 큰 경우에는 1.23E+006와 같이 출력할 수도 있다. 하지만 fixed로 지정하면 항상 123.45처럼 출력한다. | |
scientific | 실수를 항상 1.23E+006 처럼 출력한다. | |
인자가 두 개인 setf() 함수의 두번째 인자로 사용할 수 있는 값 | adjustfield | (internal | left | right)와 같은 값이다. internal과 left와 right를 비트 단위 OR 연산한 것이다. |
basefield | (dec | hex | oct)와 같은 값이다. | |
floatfield | (fixed | scienitific)과 같은 값이다. |
위의 표는 크게 세 부분으로 나뉘어졌다.
비트 마스트(bit mask)를 사용한 인자 전달
cout.setf(ios_base::showpoint | iso_base::showpos) 처럼 비트 단위 OR 연산을 사용할 수 있는 것은 각 옵션 값들이 고유한 비트 값을 가지고 있기 때문이다. 다음과 같이 각 옵션 값들이 서로 다른 한 비트만 1의 값을 갖게 지정하는 것이다. 예를 들어 다음과 같이 값을 설정하고 있다고 하자. (2)는 2진수를 의미하고, (10)은 10진수를 의미한다.
showpoint = (2) 0000 0001 = (10) 1
showpos = (2) 0000 0010 = (10) 2
uppercase = (2) 0000 0100 = (10) 4
showpoint는 첫번째 비트만 1이고, showpos는 두번째 비트만 1이다. 이런 식으로 값을 배정하면 나머지 옵션 값들도 8, 16, 32와 같이 특정 비트 하나만 1로 세팅된 값을 갖게 될 것이다. 이렇게 정해져 있다고 가졍했을 때, showpoint | showpos는 3의 값을 갖는다.
showpoint | showpos = (2) 00000001 | (2) 00000010 = (2) 00000011 = (10) 3
결국은 setf() 함수에 정수 3을 전달하는 것이다. 함수에 전달하는 인자는 단순한 정수 값 하나지만, 함수 안에서는 비트를 분석함으로써 어떤 값들이 전달됐는지 확인할 수 있다. 예를 들어 인자로 전달한 값에 showpoint를 포함했는지 확인하기 위해서 다음과 같이 할 수 있다.
fmtflags setf(fmtflags f) {
if((f & showpoint) != 0)
// showpoint 값을 포함하고 있다.
여기서 f & showpoint는 다음과 같은 의미를 갖는다.
f & showpoint = (2) 00000011 & (2) 00000001 = (2) 00000001 = (10) 1
매개 변수 f안에서 showpoint에 해당하는 비트가 1로 세팅된 경우에만 이 연산의 결과가 0이 아닌 값을 가질 수 있으므로, 이 식을 통해서 매개 변수에 showpoitn가 포함되어 있는지 확인할 수 있다. showpoint 뿐만 아니라 다른 모든 옵션 값들에 대해서도 이런 방식을 사용해서 인자를 검사할 수 있다.
이 방법은 오래 전부터 널리 사용하는 방법으로 현재까지도 많은 곳에서 사용하고 있다. 하나의 정수 값 안에 여러 가지 다양한 옵션 값들을 실어서 넘겨줄 수 있기 때문이다.
cout.setf(ios_base::hex, ios_base::basefield);
cout << 123 << endl; // 7B를 출력한다.
setf() 함수는 두 가지 버전이 있다. 두번째 그룹에 속한 옵션들은 첫번째 그룹에 속한 것들과는 성격이 조금 다르다. 두번째 그룹에 속한 옵션들은 동시에 사용할 수 없는 것들이 존재한다. 예를 들어 dec, hex, oct는 동시에 사용할 수 없다. 정수를 10진수이면서 16진수이면서 8진수로 출력할 수 없기 때문이다. 셋 중에 하나를 고르는 수 밖에 없다. internal, left, right도 같은 관계에 있고 fixed, scientific도 같은 관계에 있다. 그렇기 때문에 이 옵션들은 단순히 켰다 껐다 하는 것이 아니라, 셋이나 둘 중에 하나를 고르는 방식을 사용해야 한다. 그리고 그것이 바로 두 가지 버전의 setf() 함수가 갖는 차이점이다.
만약 다음과 같이 잘못된 버전의 setf() 함수를 사용하면 원하는 대로 동작하지 않으니 주의해야 한다.
cout.setf(ios_base::hex);
cout << 123 << endl; // 16진수로 출력하지 않는다.
표에서 첫번째 그룹에 속한 값들은 인자를 하나 받는 setf() 를 사용하면 된다고 했는데, 이때는 여러 개의 값을 동시에 지정하는 것도 가능하다. 예를 들어 showpoint, showpos를 동시에 적용하고 싶다면 setf() 함수를 두 번 호출하는 대신 다음과 같이 호출할 수 있다.
cout.setf(ios_base::showpoint | iso_base::showpos);
cout << 123.0 << endl; // +123.000을 출력한다.
이번에는 setf() 함수를 사용해서 지정한 옵션을 다시 취소하는 방법을 알아보자. 어떤 setf() 함수를 사용했느냐에 따라서 달라지는데, 인자가 두 개 있는 setf() 함수를 사용한 경우라면 setf() 함수의 반환 값을 보관해두었다가 다시 되돌리는 방법을 사용한다.
// setf() 의 반환 값을 보관
ios_base::fmtflags old_flags;
old_flags = cout.setf(ios_base::scientific, ios_base::floatfield);
cout << 12.34 << endl; // 1.234000e+001을 출력한다.
// 보관한 값으로 다시 셋팅
cout.setf(old_flags, ios_base::floatfield);
cout << 12.34 << endl; // 12.34를 출력한다.
setf() 함수는 바로 이전의 셋팅 값을 반환하기 때문에 그 값을 보관해두었다가 다시 setf() 함수를 호출하면 이전 셋팅으로 돌아갈 수 있는 것이다.
반면에 인자가 하나인 setf() 함수를 사용했다면 unsetf() 함수를 사용하면 된다.
cout.setf(iso_base::showpos);
cout << 333 << endl; // +333을 출력한다.
cout.unsetf(iso_base::showpos);
cout << 333 << endl; // 333을 출력한다.
그 밖의 함수들
width() 함수
setf() 함수 외에도 출력 형식을 지정하는 데 사용하는 함수가 몇 개 더 있는데 아주 간단한 것들이다. 처음으로 볼 것은 width() 함수로, 출력하는 값을 몇 칸에 결쳐서 출력할지를 지정하는 역할을 한다. 다음의 예제처럼 하면 숫자 555를 10칸에 걸쳐서 출력한다. 공백 7칸에 숫자 3칸을 더해 모두 10칸이 되는 것이다.
cout.width(10);
cout << 555 << ", " << 556 << endl; // _______555, 556을 출력한다.
width() 함수의 특징은 딱 한 번의 출력에 대해서만 효력이 있다는 점이다. 그래서 첫번째 정수 555만 10칸에 걸쳐 출력했고, 콤마와 55ㅣ6은 자리 자리 수대로 출력했다.
precision() 함수
precision()함수는 실수의 출력 방식에 따라서 다른 의미를 갖는데, iso_base::fixed를 지정한 경우에는 소수점 이하 자리 수를 지정하는 용도로 사용할 수 있다. 예를 들어 소수점 이하 둘째 자리까지만 출력하고 싶다면 다음과 같이 할 수 있다.
cout.setf(ios_base::fixed);
cout.precision(2);
cout << 5.55555 << endl; // 5.55처럼 출력한다.
fill() 함수
fill() 함수를 사용하면 빈 칸을 채누는 문자를 지정할 수 있다. 기본적으로 공백 문자(space)가 채워지는데, fill() 함수를 사용해서 다른 문자로 바꿀 수 있다.
cout.fill('*');
cout.width(10);
cout << 555 << endl; // *******555를 출력한다.
조종자 (Manipulator)
조정자는 위의 setf() 함수나 기타 함수들을 호출하는 방법에 지나지 않는다. 예를 들어 조정자를 사용하면 정수를 16진수로 출력하기 위해서 다음과 같이 할 수 있다.
cout << hex << 333 << endl; // 14d를 출력한다.
이전에는 hex 옵션 값을 사용하기 위해서 복잡하게 setf()를 호출했었는데 조정자를 사용하면 매우 간단하게 끝낼 수 있다. 조정자는 setf() 함수의 옵션 값들과 이름이 비슷하기 때문에 이름만 보아도 그 용도를 충분히 짐작할 수 있다. 다음 표에 조정자들의 이름을 나열해놓았다.
boolalpha | showbase | showpoint | showpos | uppercase |
noboolalpha | noshowbase | noshowpoint | noshowpos | nouppercase |
dec | hex | oct | fixed | scientific |
internal | left | right |
표를 보면 no~로 시작하는 이름들이 보이는데, 예를 들어 nobolalpha는 boolalpha를 취소하는 의미를 갖게 된다. 조종자들의 내부에서는 결국 setf()나 unsetf() 함수를 호출한다고 생각하면 된다.
여기에 더해 iomanip (Input Output Manipulator) 헤더 파일에는 인자를 받을 수 있는 조종자를 몇 개 더 정의하고 있다. 예를 들어 width() 함수를 호출하는 대신에 다음과 같이 조종자를 사용할 수 있다.
#include <iomanip)
cout << setw(10) << 555 << endl; // _______555를 출력한다.
다음은 iomanip 헤더 파일에 있는 조종자들인데 이 조종자들은 모두 인자가 있다.
값 | 의미 |
setioflags | setf()를 사용하는 것과 동일한 효과가 있다. |
resetiosflags | setf()를 통해서 지정한 옵션을 제거하는 효과가 있다. |
setfill | fill()을 호출한 효과가 있다. |
setprecision | precision()을 호출한 효과가 있다. |
setw | width()를 호출한 효과가 있다. |
setbase | 8, 10, 16 중 한 값을 넣어서 출력하는 진법을 바꿀 수 있다. |
setioflags와 resetioflags는 인자로 위에 나오는 옵션 값들을 받는데 setf() 보다 편리하게 사용할 수 있다. 예를 들어 resetioflags를 사용해서 기존의 옵션으로 되돌리는 것도 다음과 같이 간단하다. setf() 함수를 사용할 때처럼 반환 값을 보관할 필요가 없다.
cout << setioflags (ios_base::scientific) << 12.34 << endl; // 1.234000e+001
cout << resetioflags (ios_base::scientific) << 12.34 << endl; // 12.34
그 밖의 입출력 기능들
스트림의 상태
basic_ios 클래스에는 스트림의 상태를 확인할 수 있는 4개의 멤버 함수가 있다. 각 함수는 bool 값을 반환하며 반환 값의 의미는 다음과 같다.
bool good() const; // true면, 정상적인 상태
bool eof() const; // true면, 파일이나 입력의 끝에 도달
bool fail() cosnt; // true면, 예상치 못한 입력이 들어오는 등의 문제 발생
bool bad() const; // true면, 잘못된 파일 등의 문제 발생
예를 들어 현재 cin 객체의 상태가 정상적인지 알아보기 위해서 다음과 같이 검사할 수 있다.
if(cin.good()) {
// 정상
}
특히 이 함수들은 cin 객체를 사용해서 정수를 입력 받을 때 유용하게 사용할 수 있다. 정수를 입력 받으려고 하는데 사용자가 문자열을 입력한 경우에는 cin 객체가 실패 상태로 변하고 더 이상 사용할 수 없게 된다. 그렇기 때문에 cin 객체의 상태를 검사하고 다시 정상적인 상태로 되돌리는 작업을 필요로 한다.
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
clear() 함수는 cin 객체의 내부적인 상태를 다시 정상으로 돌리는 역할을 한다. 그렇기 때문에 이 함수만 호출하더라도 cin.good() 함수는 true를 반환할 것이다. 하지만 이것으로 충분하지 않다. 왜냐하면 cin 객체의 버퍼에는 아직도 잘못된 데이터가 남아있기 때문이다. 그래서 ignore() 함수를 사용해서 버퍼에 남은 데이터를 모두 무시해버릴 필요가 있다.
basic_istream& ignore(streamsize _Count, int_type _Delim);
ignore() 함수의 원형은 위와 같다. 첫번째 인자 _Count는 버퍼에서 무시하려는 데이터의 바이트수를 의미한다. 예를 들어 인자로 10을 념겨주면 버퍼에서 10바이트를 무시하고 지나간다. 다음번에 cin 객체에서 데이터를 요구하면 10바이트를 지나친 위치에서부터 데이터를 읽게 되는 것이다. streamsize는 정수 타입을 재정의한 타입에 지나지 않는다.
두번째 인자 _Delim에는 문자를 하나 넘겨주면 되는데, 이 문자를 만날 때까지만 데이터를 무시하게 된다. 예를 들어 _Count에 10을, _Delim에 '\n'을 넣어주었다고 하자. 그런데 만약 10바이트를 무시하기 전 '\n'을 만나게 되면 거기까지만 무시하게 된다. 즉, 데이터 중에 '\n'을 만나거나 10바이트가 될 때까지 무시하라는 뜻이 된다.
_Count 인자로 numeric_limits<streamsize>::max() 를 넘겨주었는데, 이 표현은 streamsize 타입이 가질 수 있는 최대 값을 반환한다. 결국 스트림에 있는 모든 데이터를 지우라는 뜻이 된다.
numric_limits<streamsize>::max() 이 표현은 조금 복잡해 보이지만 하나씩 따져보면 쉽게 이해할 수 있다. 우선은 numeric_limits가 템플릿 클래스고, 템플릿 인자로 streamsize를 넘겨주었다는 것을 알 수 있다. 그리고 max()는 numeric_limits 클래스의 정적 멤버 함수라는 것도 알 수 있다.
문자열의 입력
cin 객체를 사용해서 문자열을 입력 받을 때는 메모리 공간을 미리 확보해두어야 하는 문제가 있다. 사용자가 입력할 문자열의 길이를 예측하는 것은 불가능하기 때문이다. 그래서 동적 할당을 사용하거나 정적으로 할당하되 입력 받을 문자열의 길이를 제한하는 방법을 사용해야 한다.
파일 입출력 : ofstream, ifstream
파일 입출력에 사용되는 클래스는 ofstream과 ifstream이다. 이 클래스들 역시 실제 클래스는 아니고 다음과 같이 템플릿 클래스를 재정의한 클래스다.
typedef basic_ofstream<char> ofstream;
typedef basic_ifstream<char> ifstream;
즉 ofstream과 ifstream 객체의 실제 클래스는 basic_ofstream과 basic_ifstream이 되는 것이다. 그리고 이 클래스들은 basic_ostream과 basic_istream의 자식 클래스가 된다.
class basic_ofstream : public basic_ostream
class basic_ifstream : public basic_istream
파일 열기 옵션 지정
파일을 여는 방법은 다음과 같이 두 가지가 있다.
ofstream file1("test.txt");
ofstream file2;
file2.open("test.txt");
생성자나 open() 멤버 함수를 사용할 수 있는데 파일 이름만 제공되면 출력, 혹은 입력용으로 파일을 생성한다. 그런데 여기에 보다 구체적인 옵션을 제시할 수 있는 방법이 있다. 예를 들면 이미 존재하는 파일을 지우고 새로 만들어서 연다거나, 기존 파일의 끝에 이어서 쓸 수 있게 연다거나 하는 등의 옵션 등이다.
값 | 의미 |
app | 파일의 끝에 자료를 추가하기 위한 용도로 연다. |
ate | 파일을 열고 파일의 끝으로 이동한다. |
binary | 텍스트(text)가 아닌 바이너리(binary)로 입출력을 한다. |
in | 파일에서 값을 읽기 위한 용도로 연다. |
out | 파일에 값을 쓰기 위한 용도로 연다. |
truc | 기존 파일이 있다면 지워버리고 새 파일을 연다. |
별다른 옵션 없이 파일의 이름만을 제공해서 파일을 열면 다음과 같이 해준 것과 같은 효과가 있다. (ifstream이었다면 ios_base::in이 기본이다)
ofstream file("test.txt", ios_base::out | ios_base::trunc);
그렇기 때문에 파일 이름만 제공한다면 기존 파일이 삭제되면서 새 파일이 열리는 것이다. 예를 들어 기존 파일을 지우고 새로 만드는 대신 기존 파일의 끝에 이이서 쓰고 싶다면 다음과 같이 파일을 열면 된다. setf() 함수에서 했던 것처럼 비트 단위 OR 연산을 사용해서 여러 옵션을 한 번에 전달할 수 있다.
ofstream file("test.txt", ios_base::out | ios_base::app);
file << "test data" << endl;
파일을 연 다음에는 파일이 잘 열린 상태인지 확인할 필요가 있다. 지정한 파일이 존재하지 않거나, 파일에 문제가 있다면 실패할 수 있기 때문이다. 이때는 is_open() 함수를 사용해서 파일이 잘 열렸는지 확인해볼 수 있다.
ofstream file("test.txt");
if(false == file.is_open()) {
// 파일을 여는데 실패
}
그리고 파일을 다 사용한 후에는 close() 함수를 호출해서 파일을 닫아줄 필요가 있다.
file.close();
하지만 ofstream, ifstream 클래스의 소멸자에서 자동적으로 close() 함수를 호출하기 때문에 굳이 close() 함수를 호출해줄 필요는 없다. 물론 객체가 소멸되기 전에 함수를 빨리 닫을 필요가 있다면 직접 호출해주는 것이 좋다.
'Programming > C++' 카테고리의 다른 글
[Programming/C++] 배열의 대안 (vector, array 템플릿 클래스) (0) | 2023.02.15 |
---|---|
[Programming/C++] 문자열 상수 (0) | 2023.02.05 |
[Programming/C++] STL (Standard Template Library) (0) | 2023.01.27 |
[Programming/C++] C++ 코딩 표준 참고 사이트 (0) | 2023.01.26 |
[Programming/C++] 템플릿 (Template) (0) | 2023.01.26 |