C++ 스트림 I/O | "iostream" 완벽 가이드

C++ 스트림 I/O | "iostream" 완벽 가이드

이 글의 핵심

C++ 스트림 I/O에 대해 정리한 개발 블로그 글입니다. #include <iostream> using namespace std;

기본 입출력

#include <iostream>
using namespace std;

int main() {
    // 출력
    cout << "Hello World" << endl;
    
    // 입력
    int x;
    cin >> x;
    cout << "입력: " << x << endl;
    
    // 여러 값
    int a, b;
    cin >> a >> b;
    cout << a + b << endl;
}

포매팅

#include <iomanip>

int main() {
    double pi = 3.14159265359;
    
    // 소수점 자릿수
    cout << fixed << setprecision(2) << pi << endl;  // 3.14
    
    // 너비
    cout << setw(10) << 42 << endl;  // "        42"
    
    // 채우기
    cout << setfill('0') << setw(5) << 42 << endl;  // "00042"
    
    // 16진수
    cout << hex << 255 << endl;  // ff
    
    // 8진수
    cout << oct << 64 << endl;  // 100
    
    // 10진수
    cout << dec << 100 << endl;  // 100
}

stringstream

#include <sstream>

int main() {
    // 문자열 → 숫자
    string s = "123 456 789";
    istringstream iss(s);
    
    int a, b, c;
    iss >> a >> b >> c;
    cout << a + b + c << endl;  // 1368
    
    // 숫자 → 문자열
    ostringstream oss;
    oss << "값: " << 42 << ", " << 3.14;
    cout << oss.str() << endl;  // "값: 42, 3.14"
}

실전 예시

예시 1: CSV 파싱

#include <sstream>
#include <vector>

vector<vector<string>> parseCSV(const string& csv) {
    vector<vector<string>> result;
    istringstream iss(csv);
    string line;
    
    while (getline(iss, line)) {
        vector<string> row;
        istringstream lineStream(line);
        string cell;
        
        while (getline(lineStream, cell, ',')) {
            row.push_back(cell);
        }
        
        result.push_back(row);
    }
    
    return result;
}

int main() {
    string csv = "Alice,25,90\nBob,30,85\nCharlie,35,95";
    
    auto data = parseCSV(csv);
    
    for (const auto& row : data) {
        for (const auto& cell : row) {
            cout << cell << "\t";
        }
        cout << endl;
    }
}

예시 2: 로그 포매터

#include <sstream>
#include <iomanip>

class Logger {
public:
    template<typename... Args>
    void log(Args... args) {
        ostringstream oss;
        
        // 타임스탬프
        auto now = chrono::system_clock::now();
        auto time = chrono::system_clock::to_time_t(now);
        oss << "[" << put_time(localtime(&time), "%Y-%m-%d %H:%M:%S") << "] ";
        
        // 메시지
        (oss << ... << args);
        
        cout << oss.str() << endl;
    }
};

int main() {
    Logger logger;
    
    logger.log("서버 시작");
    logger.log("포트: ", 8080);
    logger.log("사용자 ", "Alice", " 로그인");
}

예시 3: 테이블 출력

void printTable(const vector<vector<string>>& data) {
    // 열 너비 계산
    vector<size_t> widths(data[0].size(), 0);
    
    for (const auto& row : data) {
        for (size_t i = 0; i < row.size(); i++) {
            widths[i] = max(widths[i], row[i].size());
        }
    }
    
    // 출력
    for (const auto& row : data) {
        for (size_t i = 0; i < row.size(); i++) {
            cout << left << setw(widths[i] + 2) << row[i];
        }
        cout << endl;
    }
}

int main() {
    vector<vector<string>> data = {
        {"Name", "Age", "Score"},
        {"Alice", "25", "90"},
        {"Bob", "30", "85"},
        {"Charlie", "35", "95"}
    };
    
    printTable(data);
}

예시 4: 설정 파일 파싱

#include <fstream>
#include <map>

map<string, string> loadConfig(const string& filename) {
    map<string, string> config;
    ifstream file(filename);
    string line;
    
    while (getline(file, line)) {
        // 주석 제거
        size_t commentPos = line.find('#');
        if (commentPos != string::npos) {
            line = line.substr(0, commentPos);
        }
        
        // 공백 제거
        istringstream iss(line);
        string key, value;
        
        if (getline(iss, key, '=') && getline(iss, value)) {
            config[key] = value;
        }
    }
    
    return config;
}

int main() {
    // config.txt:
    // host=localhost
    // port=8080
    // # 주석
    // timeout=30
    
    auto config = loadConfig("config.txt");
    
    for (const auto& [key, value] : config) {
        cout << key << " = " << value << endl;
    }
}

파일 I/O

#include <fstream>

int main() {
    // 쓰기
    ofstream out("output.txt");
    out << "Hello" << endl;
    out << 42 << endl;
    out.close();
    
    // 읽기
    ifstream in("output.txt");
    string line;
    
    while (getline(in, line)) {
        cout << line << endl;
    }
    
    in.close();
}

조작자

// 정렬
cout << left << setw(10) << "Left" << endl;
cout << right << setw(10) << "Right" << endl;

// 부호
cout << showpos << 42 << endl;  // +42
cout << noshowpos << 42 << endl;  // 42

// 기수 표시
cout << showbase << hex << 255 << endl;  // 0xff

// 불리언
cout << boolalpha << true << endl;  // true
cout << noboolalpha << true << endl;  // 1

자주 발생하는 문제

문제 1: cin 버퍼

// ❌ 버퍼 남음
int x;
cin >> x;
string line;
getline(cin, line);  // 빈 줄 읽음

// ✅ 버퍼 비우기
cin >> x;
cin.ignore();
getline(cin, line);

문제 2: 파일 열기 실패

// ❌ 체크 안함
ifstream file("nonexistent.txt");
string line;
getline(file, line);  // 실패

// ✅ 체크
ifstream file("nonexistent.txt");
if (!file.is_open()) {
    cerr << "파일 열기 실패" << endl;
    return 1;
}

문제 3: 스트림 상태

// 입력 실패 시 스트림 상태 확인
int x;
cin >> x;

if (cin.fail()) {
    cout << "입력 실패" << endl;
    cin.clear();  // 상태 초기화
    cin.ignore(numeric_limits<streamsize>::max(), '\n');  // 버퍼 비우기
}

FAQ

Q1: cout vs printf?

A:

  • cout: 타입 안전, C++스러움
  • printf: 빠름, C 호환

Q2: endl vs ‘\n’?

A:

  • endl: 버퍼 플러시 (느림)
  • ‘\n’: 플러시 안함 (빠름)

Q3: stringstream 성능은?

A: 문자열 연결보다 빠를 수 있습니다.

Q4: 바이너리 I/O는?

A: ios::binary 모드 사용.

Q5: 스트림 재사용은?

A: clear()와 str("") 호출.

Q6: iostream 학습 리소스는?

A:

  • cppreference.com
  • “The C++ Standard Library”
  • “C++ Primer”

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ Date Parsing & Formatting | “날짜 파싱 및 서식” 가이드
  • C++ Designated Initializers | “지정 초기화” 가이드
  • C++ Locale | “지역화” 가이드

관련 글

  • C++ 시리즈 전체 보기
  • C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
  • C++ ADL |
  • C++ Aggregate Initialization |