입출력 스트림(iostream)
기본 개념
C++의 입출력 시스템은 스트림(stream) 개념을 기반으로 한다. 스트림은 데이터가 흐르는 통로를 의미하며, C++에서는 이를 객체지향적으로 구현하여 제공한다. C와 달리 C++에서는 iostream
라이브러리를 통해 표준 입출력을 처리한다.
iostream 라이브러리 구조
C++의 입출력 시스템은 다음과 같은 클래스 계층 구조를 가진다.
ios
: 모든 입출력 클래스의 기본 클래스istream
: 입력 스트림 클래스(cin의 기반 클래스)ostream
: 출력 스트림 클래스(cout, cerr, clog의 기반 클래스)iostream
: 입출력 모두 가능한 스트림 클래스
표준 입출력 스트림 객체
C++은 iostream
헤더에 정의된 네 가지 표준 스트림 객체를 제공한다.
cin(Console INput)
cin
은 표준 입력 스트림 객체로, 키보드로부터의 입력을 처리함
#include <iostream>
using namespace std;
int main() {
int number;
string name;
cout << "숫자를 입력하세요: ";
cin >> number;
cout << "이름을 입력하세요: ";
cin >> name;
cout << "입력된 숫자: " << number << endl;
cout << "입력된 이름: " << name << endl;
return 0;
}
>>
연산자(추출 연산자)를 사용하여 데이터를 입력 받음- 공백(스페이스, 탭, 줄바꿈)을 기준으로 데이터를 구분
- 데이터 타입을 자동으로 변환
cout(Console OUTput)
cout
은 표준 출력 스트림 객체로, 콘솔 화면에 출력할 때 사용
#include <iostream>
using namespace std;
int main() {
int age = 25;
double height = 175.5;
string name = "홍길동";
cout << "이름: " << name << endl;
cout << "나이: " << age << endl;
cout << "키: " << height << "cm" << endl;
return 0;
}
<<
연산자(삽입 연산자)를 사용하여 데이터를 출력- 데이터 타입을 자동으로 변환하여 출력
- 기본적으로 버퍼링됨
cerr(Console ERRor)
cerr
은 표준 오류 출력 스트림 객체로, 오류 메시지를 출력할 때 사용
#include <iostream>
using namespace std;
int main() {
int divisor = 0;
if (divisor == 0) {
cerr << "오류: 0으로 나눌 수 없습니다." << endl;
}
return 0;
}
- 버퍼링되지 않음(출력이 즉시 화면에 표시됨)
- 오류 메시지를 출력하기 위한 목적으로 사용
- 일반적으로
cout
과 같은 콘솔 화면에 출력되지만, 리다이렉션 시cout
과 다른 곳으로 출력 가능
clog(Console LOG)
clog
는 로깅을 위한 버퍼링된 오류 출력 스트림 객체이다.
#include <iostream>
using namespace std;
int main() {
clog << "로그 메시지: 프로그램 시작" << endl;
// 프로그램 로직 수행
clog << "로그 메시지: 프로그램 종료" << endl;
return 0;
}
- 버퍼링됨(여러 로그 메시지가 모였다가 한 번에 출력될 수 있음)
- 디버깅이나 로깅목적으로 사용
cerr
과 마찬가지로 표준 오류 스트림에 연결되어 있음
입출력 조작자(manipulator)
입출력 조작자는 스트림의 형식을 제어하는 함수들로, 출력 형식을 조정하는 데 사용됨
#include <iostream>
using namespace std;
int main() {
cout << "첫 번째 줄";
cout << endl; // 개행 및 버퍼 비우기
cout << "두 번째 줄" << endl;
int num = 255;
cout << "10진수: " << dec << num << endl; // 255
cout << "16진수: " << hex << num << endl; // ff
cout << "8진수: " << oct << num << endl; // 377
return 0;
}
endl
: 개행 문자를 출력하고 버퍼를 비움(flush)dec
: 정수를 10진수로 출력hex
: 정수를 16진수로 출력oct
: 정수를 8진수로 출력flush
: 버퍼를 비움ws
: 입력 스트림에서 공백 문자를 건너뜀
고급 조작자(iomamip에 정의)
더 복잡한 형식 지정이 필요한 경우 <iomanip>
헤더를 포함
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double pi = 3.141592653589793;
cout << "기본 출력: " << pi << endl;
cout << "정밀도 설정: " << setprecision(4) << pi << endl;
cout << "고정 소수점 표기법: " << fixed << setprecision(3) << pi << endl;
cout << setw(10) << "이름" << setw(5) << "나이" << endl;
cout << setfill('-') << setw(15) << endl;
cout << setfill(' ');
cout << setw(10) << "홍길동" << setw(5) << 20 << endl;
cout << setw(10) << "김철수" << setw(5) << 25 << endl;
return 0;
}
setw(n)
: 출력 필드의 너비를 n으로 설정setprecision(n)
: 부동 소수점 수의 정밀도를 n으로 설정setfill(c)
: 필드를 채울 문자 c를 설정fixed
: 부동 소수점 수를 고정 소수점 표기법으로 출력scientific
: 부동 소수점 수를 과학적 표기법으로 출력left
: 출력을 왼쪽으로 정렬right
: 출력을 오른쪽으로 정렬setbase(n)
: 정수의 출력 기수를 설정(8, 10, 16만 가능)
파일 입출력 스트림(fstream)
C++은 파일 입출력을 위한 클래스를 <fstream>
헤더에 정의하고 있다.
주요 파일 스트림 클래스
ifstream
: 파일 입력 스트림 클래스(파일 읽기)ofstream
: 파일 출력 스트림 클래스(파일 쓰기)fstream
: 파일입출력 스트림 클래스(읽기 및 쓰기)
파일 쓰기 예제
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 파일 출력 스트림 생성
ofstream outFile("example.txt");
// 파일이 정상적으로 열렸는지 확인
if (!outFile) {
cerr << "파일을 열 수 없습니다." << endl;
return 1;
}
// 파일에 데이터 쓰기
outFile << "안녕하세요!" << endl;
outFile << "C++ 파일 입출력 예제입니다." << endl;
// 숫자 데이터 쓰기
for (int i = 1; i <= 5; i++) {
outFile << "숫자 " << i << ": " << i * i << endl;
}
// 파일 닫기
outFile.close();
cout << "파일 쓰기 완료" << endl;
return 0;
}
파일 읽기 예제
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
// 파일 입력 스트림 생성
ifstream inFile("example.txt");
// 파일이 정상적으로 열렸는지 확인
if (!inFile) {
cerr << "파일을 열 수 없습니다." << endl;
return 1;
}
// 한 줄씩 파일 읽기
string line;
while (getline(inFile, line)) {
cout << line << endl;
}
// 파일 닫기
inFile.close();
return 0;
}
파일 모드 설정
파일을 열 때 다양한 모드를 설정할 수 있다.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 기존 파일에 데이터 추가하기
ofstream outFile("example.txt", ios::app);
if (outFile) {
outFile << "이 내용은 파일 끝에 추가됩니다." << endl;
outFile.close();
}
// 이진 모드로 파일 쓰기
ofstream binFile("data.bin", ios::binary | ios::out);
if (binFile) {
int numbers[] = {1, 2, 3, 4, 5};
binFile.write(reinterpret_cast<char*>(numbers), sizeof(numbers));
binFile.close();
}
return 0;
}
ios::in
: 읽기 모드ios::out
: 쓰기 모드ios::app
: 파일 끝에 데이터 추가ios::ate
: 파일을 열자마자 파일 끝으로 이동ios::trunc
: 파일이 이미 존재하면 내용을 삭제ios::binary
: 이진 모드로 파일 입출력
파일 위치 지정자
#include <iostream>
#include <fstream>
using namespace std;
int main() {
fstream file("data.txt", ios::in | ios::out);
if (!file) {
cerr << "파일을 열 수 없습니다." << endl;
return 1;
}
// 파일 끝으로 이동
file.seekp(0, ios::end);
// 현재 위치 확인
streampos size = file.tellp();
cout << "파일 크기: " << size << " 바이트" << endl;
// 파일의 처음으로 이동
file.seekg(0, ios::beg);
// 파일의 내용 읽기
char ch;
while (file.get(ch)) {
cout << ch;
}
file.close();
return 0;
}
seekg(pos)
,seekg(offset, direction)
: 입력 스트림의 위치 지정자 이동seekp(pos)
,seekp(offset, direction)
: 출력 스트림의 위치 지정자 이동tellg()
: 입력 스트림의 현재 위치 반환tellp()
: 출력 스트림의 현재 위치 반환
스트림의 상태 확인 및 오류 처리
스트림 상태 플래그
C++ 스트림은 다음과 같은 상태 플래그를 가진다.
goodbit
: 스트림이 정상 상태eofbit
: 파일의 끝에 도달failbit
: 입출력 작업 실패badbit
: 심각한 오류 발생
스트림 상태 검사 메소드
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream file("nonexistent.txt");
// 파일이 성공적으로 열렸는지 확인
if (!file) {
cerr << "파일을 열 수 없습니다." << endl;
// 상태 플래그 확인
if (file.fail()) {
cerr << "failbit가 설정되었습니다." << endl;
}
if (file.bad()) {
cerr << "badbit가 설정되었습니다." << endl;
}
return 1;
}
int number;
file >> number;
// 입력 작업이 성공했는지 확인
if (file.good()) {
cout << "입력 성공: " << number << endl;
} else {
cerr << "입력 실패" << endl;
// 실패 원인 확인
if (file.eof()) {
cerr << "파일의 끝에 도달했습니다." << endl;
}
if (file.fail()) {
cerr << "형식 불일치 등의 이유로 입력 실패했습니다." << endl;
// 오류 상태 초기화
file.clear();
}
}
file.close();
return 0;
}
good()
: 스트림이 정상 상태인지 확인eof()
: 파일의 끝에 도달했는지 확인fail()
: 입출력 작업이 실패했는지 확인bad()
: 심각한 오류가 발생했는지 확인clear()
: 스트림의 상태 플래그를 초기화
요약
C++의 입출력 스트림 시스템은 C에 비해 더 객체 지향적이고 확장성이 높은 입출력 방식을 제공한다.
C에서 C++로 전환시 printf
, scanf
등의 함수 대신 iostream
라이브러리의 스트림 객체와 연산자를 사용하는 것이 좋다. 이는 코드의 가독성과 유지보수성을 향상시키고, 타입 안정성을 높이는 데 도움이 된다.