콘텐츠로 이동

C++ 입출력 라이브러리 완벽 가이드

개요

C++의 입출력 시스템은 iostream 라이브러리를 중심으로 구성되며, 타입 안전하고 확장 가능한 스트림 기반 I/O를 제공합니다.

목차


기본 입출력

iostream 기본 객체

#include <iostream>
using namespace std;

// 표준 입출력 객체
cin     // 표준 입력
cout    // 표준 출력
cerr    // 표준 오류 (버퍼링 안됨)
clog    // 표준 로그 (버퍼링 됨)

기본 출력 (cout)

#include <iostream>

// 기본 출력
cout << "Hello World!" << endl;
cout << "Number: " << 42 << "\n";

// 연쇄 출력
cout << "Name: " << name << ", Age: " << age << endl;

// 다양한 타입 출력
int num = 100;
double pi = 3.14159;
char grade = 'A';
string text = "Hello";

cout << num << " " << pi << " " << grade << " " << text << endl;

// endl vs '\n'
cout << "Line 1" << endl;  // 개행 + 버퍼 플러시
cout << "Line 2\n";        // 개행만 (더 빠름)

기본 입력 (cin)

#include <iostream>
#include <string>

// 기본 입력
int number;
cin >> number;

string word;
cin >> word;  // 공백까지만 읽음

// 여러 값 입력
int a, b, c;
cin >> a >> b >> c;

// 전체 줄 입력
string line;
getline(cin, line);         // 전체 줄 읽기
getline(cin, line, ',');    // 특정 구분자까지 읽기

// 입력 버퍼 비우기
cin.ignore();               // 1문자 무시
cin.ignore(1000, '\n');     // 개행까지 무시

고급 입력 기법

#include <iostream>
#include <limits>

// 안전한 입력
int safe_input() {
    int value;
    while (!(cin >> value)) {
        cout << "Invalid input. Try again: ";
        cin.clear();                    // 오류 플래그 제거
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }
    return value;
}

// 문자 단위 입력
char ch;
ch = cin.get();             // 1문자 읽기 (공백, 개행 포함)
cin.get(ch);                // 참조로 읽기
cin.putback(ch);            // 문자 되돌리기
cin.peek();                 // 다음 문자 확인 (읽지 않음)

// 블록 입력
char buffer[100];
cin.read(buffer, 99);       // 최대 99문자 읽기
streamsize bytes_read = cin.gcount(); // 실제 읽은 바이트 수

파일 입출력

ifstream (파일 읽기)

#include <fstream>
#include <iostream>
#include <string>

// 파일 열기
ifstream file("input.txt");
ifstream file("input.txt", ios::in); // 명시적 읽기 모드

// 열기 확인
if (!file.is_open()) {
    cerr << "파일을 열 수 없습니다!" << endl;
    return -1;
}

// 다양한 읽기 방법
string line;
while (getline(file, line)) {       // 줄 단위 읽기
    cout << line << endl;
}

// 단어 단위 읽기
string word;
while (file >> word) {
    cout << word << " ";
}

// 전체 파일 읽기
string content((istreambuf_iterator<char>(file)),
                istreambuf_iterator<char>());

file.close(); // 자동으로 소멸자에서 호출되지만 명시적 호출 권장

ofstream (파일 쓰기)

#include <fstream>

// 파일 쓰기 (덮어쓰기)
ofstream file("output.txt");
ofstream file("output.txt", ios::out);

// 파일 쓰기 (추가 모드)
ofstream file("output.txt", ios::app);

// 쓰기
file << "Hello World!" << endl;
file << "Number: " << 42 << endl;

// 서식 적용
file << fixed << setprecision(2) << 3.14159 << endl;

file.close();

fstream (읽기/쓰기)

#include <fstream>

// 읽기/쓰기 모드
fstream file("data.txt", ios::in | ios::out);

// 파일이 없으면 생성
fstream file("data.txt", ios::in | ios::out | ios::app);

// 파일 포인터 조작
file.seekg(0, ios::beg);     // 읽기 포인터를 처음으로
file.seekp(0, ios::end);     // 쓰기 포인터를 끝으로
streampos pos = file.tellg(); // 현재 읽기 포인터 위치

파일 모드

ios::in      // 읽기
ios::out     // 쓰기
ios::app     // 추가 (파일 끝에서 쓰기)
ios::ate     // 파일 끝으로 이동
ios::trunc   // 파일 내용 삭제
ios::binary  // 바이너리 모드

// 조합 사용
ofstream file("data.txt", ios::out | ios::app);
ifstream file("data.bin", ios::in | ios::binary);

바이너리 파일 처리

#include <fstream>

struct Person {
    char name[50];
    int age;
    double salary;
};

// 바이너리 쓰기
ofstream file("person.dat", ios::binary);
Person p = {"John Doe", 30, 50000.0};
file.write(reinterpret_cast<const char*>(&p), sizeof(Person));

// 바이너리 읽기
ifstream file("person.dat", ios::binary);
Person p;
file.read(reinterpret_cast<char*>(&p), sizeof(Person));

문자열 스트림

istringstream (문자열에서 읽기)

#include <sstream>

string data = "123 45.6 Hello";
istringstream iss(data);

int num;
double d;
string word;

iss >> num >> d >> word;
cout << num << ", " << d << ", " << word << endl;

// 줄 단위 파싱
string line = "apple,banana,cherry";
istringstream iss(line);
string item;
vector<string> tokens;

while (getline(iss, item, ',')) {
    tokens.push_back(item);
}

ostringstream (문자열로 출력)

#include <sstream>

ostringstream oss;
oss << "Number: " << 42 << ", Pi: " << 3.14159;
string result = oss.str();
cout << result << endl;

// 문자열 포맷팅
string format_number(int num, double value) {
    ostringstream oss;
    oss << "ID: " << setfill('0') << setw(5) << num
        << ", Value: " << fixed << setprecision(2) << value;
    return oss.str();
}

stringstream (읽기/쓰기)

#include <sstream>

stringstream ss;
ss << "Hello " << 123;
ss << " World";

string result = ss.str(); // "Hello 123 World"

// 내용 변경
ss.str("New content");
ss.clear(); // 스트림 상태 플래그 초기화

서식 지정

iomanip 헤더

#include <iomanip>

// 폭 지정
cout << setw(10) << "Hello" << endl;        // 우측 정렬
cout << left << setw(10) << "Hello" << endl; // 좌측 정렬
cout << setfill('*') << setw(10) << 123 << endl; // 빈 공간 채우기

// 정밀도
cout << setprecision(3) << 3.14159 << endl; // 3.14
cout << fixed << setprecision(2) << 3.14159 << endl; // 3.14
cout << scientific << 3.14159 << endl;      // 3.141590e+00

// 진법
cout << hex << 255 << endl;     // ff
cout << oct << 255 << endl;     // 377
cout << dec << 255 << endl;     // 255

// 불린값
cout << boolalpha << true << endl;  // true
cout << noboolalpha << true << endl; // 1

플래그 직접 조작

// 현재 플래그 저장
ios_base::fmtflags old_flags = cout.flags();

// 플래그 설정
cout.setf(ios::fixed | ios::showpoint);
cout.precision(2);
cout << 3.14159 << endl; // 3.14

// 플래그 복원
cout.flags(old_flags);

// 특정 플래그만 해제
cout.unsetf(ios::fixed);

커스텀 조작자

// 간단한 조작자
ostream& tab(ostream& os) {
    return os << '\t';
}

cout << "Name" << tab << "Age" << tab << "Score" << endl;

// 매개변수가 있는 조작자
struct setcolor {
    string color;
    setcolor(const string& c) : color(c) {}
};

ostream& operator<<(ostream& os, const setcolor& sc) {
    // ANSI 색상 코드 (예시)
    if (sc.color == "red") os << "\033[31m";
    else if (sc.color == "green") os << "\033[32m";
    else if (sc.color == "reset") os << "\033[0m";
    return os;
}

// 사용법
cout << setcolor("red") << "Error!" << setcolor("reset") << endl;

스트림 상태

상태 플래그

#include <iostream>

// 상태 확인
if (cin.good())     // 모든 상태가 정상
if (cin.bad())      // 복구 불가능한 오류
if (cin.fail())     // 복구 가능한 오류 (입력 형식 오류 등)
if (cin.eof())      // 파일 끝

// 상태 초기화
cin.clear();        // 모든 오류 플래그 제거
cin.clear(ios::goodbit); // 특정 상태로 설정

// 상태 설정
cin.setstate(ios::failbit); // fail 상태 설정

오류 처리 예제

int safe_read_int() {
    int value;
    while (true) {
        cout << "Enter an integer: ";
        if (cin >> value) {
            break; // 성공
        } else {
            cout << "Invalid input. Try again." << endl;
            cin.clear(); // 오류 플래그 제거
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        }
    }
    return value;
}

// 파일 읽기 오류 처리
void read_file(const string& filename) {
    ifstream file(filename);

    if (!file) {
        cerr << "Cannot open file: " << filename << endl;
        return;
    }

    string line;
    while (getline(file, line)) {
        if (file.bad()) {
            cerr << "Read error occurred" << endl;
            break;
        }
        cout << line << endl;
    }

    if (file.eof()) {
        cout << "File read successfully" << endl;
    } else if (file.fail()) {
        cerr << "Format error occurred" << endl;
    }
}

성능 최적화

동기화 해제

#include <iostream>

// C stdio와 동기화 해제 (빠른 입출력)
ios::sync_with_stdio(false);
cin.tie(nullptr);

// 주의: printf/scanf와 혼용 불가

버퍼링 제어

// 출력 버퍼 강제 플러시
cout << "Hello" << flush;
cout << "World" << endl;  // 개행 + 플러시

// 입력과 출력 분리
cin.tie(nullptr);  // cin과 cout 분리

// 버퍼 크기 설정
char buffer[8192];
cout.rdbuf()->pubsetbuf(buffer, 8192);

빠른 입출력 템플릿

class FastIO {
public:
    FastIO() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cout.tie(nullptr);
    }

    template<typename T>
    FastIO& operator>>(T& value) {
        cin >> value;
        return *this;
    }

    template<typename T>
    FastIO& operator<<(const T& value) {
        cout << value;
        return *this;
    }

    FastIO& operator<<(ostream& (*f)(ostream&)) {
        cout << f;
        return *this;
    }
};

FastIO fio;
int n;
fio >> n << "Number: " << n << endl;

실전 예제

1. CSV 파일 파싱

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

struct Record {
    string name;
    int age;
    double salary;
};

vector<Record> parse_csv(const string& filename) {
    vector<Record> records;
    ifstream file(filename);
    string line;

    // 헤더 건너뛰기
    getline(file, line);

    while (getline(file, line)) {
        istringstream iss(line);
        string token;
        Record record;

        // Name
        getline(iss, record.name, ',');

        // Age
        getline(iss, token, ',');
        record.age = stoi(token);

        // Salary
        getline(iss, token);
        record.salary = stod(token);

        records.push_back(record);
    }

    return records;
}

2. 로그 파일 생성기

#include <iostream>
#include <fstream>
#include <iomanip>
#include <chrono>
#include <ctime>

class Logger {
private:
    ofstream logfile;

public:
    Logger(const string& filename) : logfile(filename, ios::app) {
        if (!logfile.is_open()) {
            throw runtime_error("Cannot open log file");
        }
    }

    template<typename... Args>
    void log(const string& level, Args&&... args) {
        // 현재 시간
        auto now = chrono::system_clock::now();
        auto time_t = chrono::system_clock::to_time_t(now);

        logfile << "[" << put_time(localtime(&time_t), "%Y-%m-%d %H:%M:%S")
                << "] [" << level << "] ";

        ((logfile << args << " "), ...);
        logfile << endl;
        logfile.flush();
    }

    template<typename... Args>
    void info(Args&&... args) { log("INFO", args...); }

    template<typename... Args>
    void error(Args&&... args) { log("ERROR", args...); }

    template<typename... Args>
    void warning(Args&&... args) { log("WARNING", args...); }
};

// 사용 예
Logger logger("app.log");
logger.info("Application started");
logger.error("Connection failed, code:", 404);

3. 설정 파일 파서

#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <string>

class ConfigParser {
private:
    map<string, string> config;

public:
    bool load(const string& filename) {
        ifstream file(filename);
        if (!file.is_open()) return false;

        string line;
        while (getline(file, line)) {
            // 주석과 빈 줄 무시
            if (line.empty() || line[0] == '#') continue;

            // key=value 파싱
            size_t pos = line.find('=');
            if (pos != string::npos) {
                string key = line.substr(0, pos);
                string value = line.substr(pos + 1);

                // 공백 제거
                key.erase(0, key.find_first_not_of(" \t"));
                key.erase(key.find_last_not_of(" \t") + 1);
                value.erase(0, value.find_first_not_of(" \t"));
                value.erase(value.find_last_not_of(" \t") + 1);

                config[key] = value;
            }
        }
        return true;
    }

    string get_string(const string& key, const string& default_value = "") {
        auto it = config.find(key);
        return (it != config.end()) ? it->second : default_value;
    }

    int get_int(const string& key, int default_value = 0) {
        auto it = config.find(key);
        return (it != config.end()) ? stoi(it->second) : default_value;
    }

    double get_double(const string& key, double default_value = 0.0) {
        auto it = config.find(key);
        return (it != config.end()) ? stod(it->second) : default_value;
    }

    bool get_bool(const string& key, bool default_value = false) {
        auto it = config.find(key);
        if (it == config.end()) return default_value;

        string value = it->second;
        transform(value.begin(), value.end(), value.begin(), ::tolower);
        return (value == "true" || value == "1" || value == "yes");
    }
};

4. 진행률 표시기

#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>

class ProgressBar {
private:
    int total;
    int current;
    int bar_width;

public:
    ProgressBar(int total, int width = 50) 
        : total(total), current(0), bar_width(width) {}

    void update(int value) {
        current = value;
        float progress = (float)current / total;
        int pos = bar_width * progress;

        cout << "[";
        for (int i = 0; i < bar_width; ++i) {
            if (i < pos) cout << "=";
            else if (i == pos) cout << ">";
            else cout << " ";
        }
        cout << "] " << int(progress * 100.0) << "%\r";
        cout.flush();
    }

    void finish() {
        cout << endl;
    }
};

// 사용 예
void demo_progress() {
    ProgressBar bar(100);
    for (int i = 0; i <= 100; ++i) {
        bar.update(i);
        this_thread::sleep_for(chrono::milliseconds(50));
    }
    bar.finish();
}

5. 메모리 사용량 모니터

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>

class MemoryMonitor {
public:
    struct MemInfo {
        size_t virtual_memory = 0;
        size_t physical_memory = 0;
    };

    static MemInfo get_memory_usage() {
        MemInfo info;

#ifdef _WIN32
        // Windows 구현
        PROCESS_MEMORY_COUNTERS pmc;
        if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
            info.virtual_memory = pmc.PagefileUsage;
            info.physical_memory = pmc.WorkingSetSize;
        }
#else
        // Linux 구현
        ifstream status("/proc/self/status");
        string line;

        while (getline(status, line)) {
            if (line.substr(0, 6) == "VmSize") {
                istringstream iss(line);
                string key, unit;
                iss >> key >> info.virtual_memory >> unit;
                info.virtual_memory *= 1024; // KB to bytes
            } else if (line.substr(0, 6) == "VmRSS:") {
                istringstream iss(line);
                string key, unit;
                iss >> key >> info.physical_memory >> unit;
                info.physical_memory *= 1024; // KB to bytes
            }
        }
#endif

        return info;
    }

    static void print_memory_usage() {
        auto info = get_memory_usage();
        cout << "Memory Usage:" << endl;
        cout << "  Virtual:  " << format_bytes(info.virtual_memory) << endl;
        cout << "  Physical: " << format_bytes(info.physical_memory) << endl;
    }

private:
    static string format_bytes(size_t bytes) {
        const char* units[] = {"B", "KB", "MB", "GB", "TB"};
        int unit = 0;
        double size = bytes;

        while (size >= 1024 && unit < 4) {
            size /= 1024;
            unit++;
        }

        ostringstream oss;
        oss << fixed << setprecision(2) << size << " " << units[unit];
        return oss.str();
    }
};

마지막 업데이트: 2025-06-24