stream 클래스 상속 관계

C++의 입출력 라이브러리는 스트림(Stream) 클래스를 기반으로 구현되어 있는데,

이 스트림 클래스는 데이터를 읽고 쓰는 데 필요한 기본적인 인터페이스를 제공하며,

이를 상속하는 다양한 클래스를 통해 입출력 기능을 확장할 수 있음

 

가장 기본이 되는 스트림 클래스는 istream, ostream, iostream 가 있음

istream은 입력 스트림을, ostream은 출력 스트림을, iostream은 입력 및 출력을 모두 지원하는 스트림 클래스

 

이들 클래스는 각각 ifstream, ofstream, fstream 클래스에서 상속받아 파일 입출력을 지원.

istringstream, ostringstream, stringstream 클래스에서 상속받아 문자열을 입출력하는 기능을 지원.

 

스트림 클래스 상속 구조를 이용하면, 입출력 기능을 다양한 방법으로 확장 가능

예를 들어, istream 클래스에서 상속받아 구현한 새로운 클래스를 통해, 입력 데이터를 다른 형식으로 파싱하거나, 입력 스트림에서 특정 패턴을 찾아 처리할 수 있습니다. 마찬가지로, ostream 클래스에서 상속받아 구현한 새로운 클래스를 통해, 출력 데이터를 다른 형식으로 변환하거나, 출력 스트림에서 특정 패턴을 찾아 처리할 수 있음

 

1) istream 클래스에서 상속받아 구현한 새로운 클래스를 통해, 입력 데이터를 다른 형식으로 파싱하는 예제

 

#include <iostream>
#include <sstream>

class MyInputStream : public std::istream {
public:
    MyInputStream(std::streambuf* buf) : std::istream(buf) {}

    int readInt() {
        int n;
        *this >> n; // 입력 스트림에서 정수를 읽어옴
        std::string s = std::to_string(n); // 정수를 문자열로 변환
        std::reverse(s.begin(), s.end()); // 문자열을 뒤집음
        return std::stoi(s); // 뒤집은 문자열을 다시 정수로 변환하여 반환
    }
};

int main() {
    std::istringstream iss("1234");
    MyInputStream mis(iss.rdbuf()); // MyInputStream 객체 생성

    int n = mis.readInt(); // MyInputStream 클래스에서 추가한 readInt() 함수 사용
    std::cout << "Reversed integer: " << n << std::endl; // 출력 결과: Reversed integer: 4321

    return 0;
}

MyInputStream :  istream 클래스에서 상속받아 정수를 읽어오는 readInt() 함수를 추가한 새로운 클래스

readInt 함수:  입력 스트림에서 정수를 읽어와 문자열로 변환한 후, 문자열을 뒤집어 다시 정수로 변환하여 반환

 

main() 함수에서는 istringstream 객체를 생성하고, 이를 MyInputStream 클래스의 객체로 래핑하여 입력 데이터를 처리.

이 때 MyInputStream 클래스에서 추가한 readInt() 함수를 사용하여 입력 데이터를 다른 형식으로 파싱

 

 

#include <iostream>
#include <sstream>

class MyInputStream : public std::istream {
public:
    MyInputStream(std::streambuf* buf) : std::istream(buf) {}

    MyInputStream& operator>>(int& n) {
        std::string s;
        *this >> s; // 입력 스트림에서 문자열을 읽어옴
        if (s[0] == '-') { // 입력값이 음수인 경우
            std::reverse(s.begin() + 1, s.end()); // 음수 부호를 제외한 문자열을 뒤집음
            n = -std::stoi(s); // 뒤집은 문자열을 음수로 변환하여 반환
        }
        else { // 입력값이 양수인 경우
            std::reverse(s.begin(), s.end()); // 문자열을 뒤집음
            n = std::stoi(s); // 뒤집은 문자열을 정수로 변환하여 반환
        }
        return *this;
    }
};

int main() {
    std::istringstream iss("-1234");
    MyInputStream mis(iss.rdbuf()); // MyInputStream 객체 생성

    int n;
    mis >> n; // MyInputStream 클래스에서 추가한 >> 연산자 사용
    std::cout << "Reversed integer: " << n << std::endl; // 출력 결과: Reversed integer: -4321

    return 0;
}

MyInputStream : istream 클래스에서 상속받아 >> 연산자 오버로딩을 추가한 새로운 클래스

operator>> 함수 : 입력 스트림에서 문자열을 읽어와 음수인 경우 음수 부호를 제외한 문자열을 뒤집고 음수로 변환하거나, 양수인 경우 문자열을 뒤집어 정수로 변환하여 반환

main() 함수에서는 istringstream 객체를 생성하고, 이를 MyInputStream 클래스의 객체로 래핑하여 입력 데이터를 처리

이 때 MyInputStream 클래스에서 추가한 >> 연산자를 사용하여 입력 데이터를 다른 형식으로 파싱

 

 

 

2) 입력 스트림에서 특정 패턴을 찾아 처리

입력 스트림에서 특정 패턴을 찾아 처리하는 예제 코드

이 예제에서는 입력 스트림에서 "[[name]] : [age]" 형태의 패턴을 찾아, name과 age를 출력

 

 

#include <iostream>
#include <string>
#include <regex>

int main() {
    std::string input = "John : 30\n[[Sarah]] : 25\n[[David]] : 40\nMary : 35";

    std::regex pattern(R"(\[\[(\w+)\]\] : (\d+))");
    std::smatch match;

    std::string::const_iterator search_start(input.cbegin());
    while (std::regex_search(search_start, input.cend(), match, pattern)) {
        std::cout << "Name: " << match[1].str() << ", Age: " << match[2].str() << std::endl;
        search_start = match.suffix().first; // 처리한 패턴 뒷부분으로 검색 시작 위치를 갱신함
    }

    return 0;
}

 

 

std::regex 클래스를 사용하여 "[[name]] : [age]" 형태의 패턴을 정규 표현식으로 표현

이후 std::regex_search 함수를 사용하여 입력 문자열에서 패턴을 찾아 match 객체에 저장

while 루프에서는 검색 시작 위치를 갱신하면서 계속해서 패턴을 찾아 처리

std::smatch 객체의 [] 연산자를 사용하여 패턴에서 추출한 name과 age 값을 출력

입력 문자열에서 "[[name]] : [age]" 형태의 패턴이 여러 번 나오기 때문에, while 루프를 사용하여 모든 패턴을 찾아 처리

이때 search_start 변수를 사용하여 처리한 패턴 뒷부분으로 검색 시작 위치를 갱신

 

 

 

입력 스트림에서 특정 패턴을 찾아 처리하는 좀 더 복잡한 예제 코드

이 예제에서는 입력 스트림에서 "Name: [name]\nAge: [age]\n" 형태의 패턴을 찾아, name과 age를 출력

이때 입력 스트림으로부터 데이터를 읽는 과정에서 에러가 발생하는 경우, 해당 에러 메시지를 출력하고 다음 데이터를 처리

 

 

#include <iostream>
#include <sstream>
#include <string>
#include <regex>

int main() {
    std::string input = "Name: John\nAge: 30\nName: Sarah\nAge: 25\nName: David\nAge: 40\nName: Mary\nAge: invalid\nName: Tom\n";

    std::istringstream stream(input);
    std::regex pattern(R"(Name: (\w+)\nAge: (\d+))");
    std::smatch match;

    std::string name, age;
    std::string line;
    while (std::getline(stream, line)) {
        if (std::regex_match(line, match, pattern)) {
            name = match[1].str();
            age = match[2].str();
            std::cout << "Name: " << name << ", Age: " << age << std::endl;
        } else if (line.find("Name: ") == 0) {
            std::cerr << "Invalid input: " << line << std::endl;
        }
    }

    return 0;
}

 

 

 

입력 문자열에서 "Name: [name]\nAge: [age]\n" 형태의 패턴을 std::regex 클래스를 사용하여 정규 표현식으로 표현

이후 std::getline 함수를 사용하여 입력 스트림에서 데이터를 한 줄씩 읽어와 처리

while 루프에서는 std::getline 함수를 사용하여 입력 스트림에서 한 줄씩 데이터를 읽어옴

이때 std::regex_match 함수를 사용하여 읽어온 데이터가 패턴과 일치하는지 확인

일치하는 경우에는 match 객체에 저장된 name과 age 값을 출력

패턴과 일치하지 않는 데이터가 입력 스트림에서 읽혀올 경우에는 에러 메시지를 출력

이때, 입력 스트림으로부터 다음 데이터를 처리하기 위해 std::cerr 객체를 사용하여 에러 메시지를 출력

위 예제 코드에서는 입력 스트림으로부터 데이터를 읽는 과정에서 에러가 발생하는 경우, 해당 에러 메시지를 출력하고 다음 데이터를 처리 이를 통해 입력 데이터의 오류를 검출하고 처리할 수 있음

 

ostream 클래스에서 상속받아 구현한 새로운 클래스를 통해, 출력 데이터를 다른 형식으로 변환하는 예제

 

ostream 클래스에서 상속받아 구현한 새로운 클래스를 통해, 출력 데이터를 다른 형식으로 변환하는 예제입니다. 이 예제는 std::ostream을 상속받아 std::ostream으로 출력되는 데이터를 16진수로 변환하여 출력하는 HexOstream 클래스를 구현한 예제

 

 

#include <iostream>
#include <iomanip>

class HexOstream : public std::ostream
{
public:
    HexOstream(std::ostream& os) : std::ostream(os.rdbuf()) {}

    HexOstream& operator<<(int value)
    {
        std::ostream& os = *this;
        os << std::hex << std::setw(2) << std::setfill('0') << value;
        return *this;
    }
};

int main()
{
    HexOstream hex_os(std::cout);

    int value = 123;
    hex_os << "value in hex: ";
    hex_os << value;

    return 0;
}

 

 

위 예제에서 HexOstream 클래스는 std::ostream 클래스를 상속받아 구현되

HexOstream 클래스는 operator<< 연산자를 오버로딩하여 16진수로 변환하여 출력

HexOstream 클래스의 operator<< 연산자는 인자로 받은 int 타입의 value 변수를 16진수로 변환하여 출력

이를 위해 std::hex 조작자를 이용하여 16진수 출력 모드로 변경하고, std::setw와 std::setfill 조작자를 이용하여 출력 폭을 맞추고 빈 자리는 0으로 채움

main() 함수에서는 HexOstream 클래스를 생성하여 std::cout 스트림을 인자로 전달

그리고 int 타입의 value 변수를 생성하고, HexOstream 클래스를 이용하여 value 변수를 출력

 

 

출력 결과

value in hex: 7b

 

 

std::ostream을 상속받아 사용자 정의 출력 스트림 클래스 MyOstream을 구현한 예제

MyOstream 클래스는 기존 출력 스트림에 대해 스트림 버퍼를 이용해 대문자로 변환하여 출력하는 기능을 추가

 

 

#include <iostream>
#include <streambuf>
#include <locale>
#include <algorithm>

class MyOstream : public std::ostream {
public:
    MyOstream(std::ostream& stream)
        : std::ostream(stream.rdbuf()), _streambuf(stream.rdbuf())
    {}

    MyOstream& operator<< (const char* str) {
        std::transform(str, str + strlen(str), str, [](char c) {
            return std::toupper(c, std::locale());
        });

        _streambuf->sputn(str, strlen(str));
        return *this;
    }

private:
    std::streambuf* _streambuf;
};

int main() {
    MyOstream myout(std::cout);
    myout << "Hello, world!" << std::endl;
    return 0;
}

MyOstream 클래스는 std::ostream을 상속받아 구현되

생성자에서 std::ostream으로부터 스트림 버퍼를 가져오며, operator<< 연산자를 오버로딩하여 출력할 문자열을 대문자로 변환하여 출력

operator<< 연산자에서는 입력으로 받은 문자열을 std::transform 알고리즘을 이용하여 대문자로 변환

그리고 스트림 버퍼에 변환된 문자열을 써넣어 출력

main() 함수에서는 MyOstream 클래스를 이용하여 std::cout으로 출력하는 myout 객체를 생성하고, myout 객체를 이용하여 "Hello, world!" 문자열을 출력합니다.

 

 

출력 결과

HELLO, WORLD!

 

 

이와 같이 std::ostream 클래스를 상속받아 사용자 정의 출력 스트림 클래스를 구현하면,

기존의 출력 스트림에 기능을 추가하거나 사용자 정의 출력 형식을 지원할 수 있음

 

 

ostream 클래스에서 상속받아 구현한 새로운 클래스를 통해, 출력 스트림에서 특정 패턴을 찾아 처리하는 예제

 

std::ostream을 상속받아 사용자 정의 출력 스트림 클래스 MyOstream을 구현한 예제

MyOstream 클래스는 기존 출력 스트림에 대해 스트림 버퍼를 이용해 특정 패턴을 찾아서 대체 문자열로 변환하는 기능을 추가

 

 

#include <iostream>
#include <streambuf>
#include <cstring>

class MyOstream : public std::ostream {
public:
    MyOstream(std::ostream& stream, const std::string& pattern, const std::string& replace)
        : std::ostream(stream.rdbuf()), _streambuf(stream.rdbuf()), _pattern(pattern), _replace(replace)
    {}

    MyOstream& operator<< (const char* str) {
        std::string s(str);
        size_t pos = s.find(_pattern);
        while (pos != std::string::npos) {
            s.replace(pos, _pattern.length(), _replace);
            pos = s.find(_pattern, pos + _replace.length());
        }

        _streambuf->sputn(s.c_str(), s.length());
        return *this;
    }

private:
    std::streambuf* _streambuf;
    std::string _pattern;
    std::string _replace;
};

int main() {
    MyOstream myout(std::cout, "world", "WORLD");
    myout << "Hello, world!" << std::endl;
    return 0;
}

 

 

MyOstream 클래스는 std::ostream을 상속받아 구현되었습니다. 생성자에서는 스트림 버퍼와 대체할 패턴과 문자열을 지정 operator<< 연산자를 오버로딩하여 출력할 문자열에서 패턴을 찾아서 대체 문자열로 변환

operator<< 연산자에서는 입력으로 받은 문자열에서 std::string::find 함수를 이용하여 패턴을 찾음

패턴이 있으면 std::string::replace 함수를 이용하여 대체 문자열로 변환

이후 다시 std::string::find 함수를 호출하여 패턴을 찾습니다. 이 과정을 반복하여 패턴이 더 이상 없을 때까지 대체

마지막으로 변환된 문자열을 스트림 버퍼에 써넣어 출력

main() 함수에서는 MyOstream 클래스를 이용하여 std::cout으로 출력하는 myout 객체를 생성하고,

myout 객체를 이용하여 "Hello, world!" 문자열을 출력

 

 

출력 결과

HELLO, WORLD!

 

+ Recent posts