c++ 입출력 버퍼와 스트림

공팡팡·2024년 9월 26일

C++

목록 보기
1/4

Input/Output Class

C++의 입출력은 ios_base라는 클래스가 담당한다. 이는 10진수 16진수 혹은 자료형 타입에 따른 처리를 결정한다.

ios클래스는 스트림 버퍼를 초기화하며, 이때 버퍼는 데이터를 내보내거나 받아들이기전에 놔둘 수 있는 임시 저장공간이다.

다음은 istream 클래스다. cin>>a cout<<a를 생각해보면 a는 어떤 타입이 들어가도 정상적으로 입출력 되던것이 떠오를 것이다. 이에 대한 오버라이딩이 되어있는 클래스가 istream이다.

Buffer

istream의 cin은 공백을 구분자로 동작하는 입력방식이기에, 공백을 만나면 입력을 종료하고, 그 이전의 단어만 저장한다. 즉 단어 단위로 입력을 받는다.

int main()
{
    std::string s;
    
        std::cin>>s;
        std::cout<<"word:"<<s<<std::endl;
}
input -> this is a long sentence
word:this

해당 코드의 cout결과는 this에서 종료된다. 즉 공백을 기준으로 this까지는 출력해내지만, 버퍼의 이동은 is쪽으로 이동하는 것으로 끝을 내는 것이다.
(getline은 이와 달리 공백을 포함하여 버퍼를 처리리하기에 한번에 출력해낼 수 있다.)

int main()
{
    std::string s;
    while(true)
    {
        std::cin>>s;
        std::cout<<"word:"<<s<<std::endl;
    }
}
///
input -> this is a long sentence
this is a long sentence
word:this
word:is
word:a
word:long
word:sentence

while문으로 바뀔 경우 공백을 기준으로 각 단어들이 출력된다. 여기서 의문을 가지는 이도 있을 것이다.

첫번째 while의 버퍼가 is로 이동하는 것은 알겠는데, 왜 is는 cin을 거치지 않고 바로 출력되는가?

이는 버퍼의 특징때문이다. 입력버퍼는 사용자가 입력한 데이터를 저장하고, 프로그램이 필요한만큼 꺼내 쓴다. 즉 한 번 입력한 문자열은 버퍼에 계속 남아있다.

앞선 예시처럼, this is a long sentence의 경우 두번째 루프가 시작되면 cin>>s는 다시 입력을 받는 것이 아닌, 입력 버퍼에 남아있는 데이터 is a long sentence라는 데이터들을 바로 처리한다.

버퍼가 비워질때 까지는 입력을 새로 받지 않고, 남은 데이터를 계속 처리하게 된다는 것이다.

StreamBuffer Flag

앞서말한 ios클래스는 스트림버퍼를 관리한다고 하였다. 스트림에는 4개의 플래그가 있는데, 이 플래그를 통해 스트림의 상태를 알 수 있다.

  • goodbit
  • badbit
  • failbit
  • eofbit

버퍼를 받는 도중에 오류가 생기면 failbit가 켜지게 된다. 아래 예시를 보자.

int main() {
    int n;
    while (std::cin >> n) {
        std::cout << "input :: " << n << std::endl;
        
    }
}
///
3
input :: 3
4
input :: 4
s

int형으로 정의된 inputdata는 3과 4를 받을때는 이상이 없었으나 정의되지 않은 char타입을 받으니 무한루프가 돌아버린다. failbit가 켜진 것이다.

failbit가 켜지게 되면 입력을 더 이상 받지 않게 되므로 프로그램이 종료는 되지 않으나, 더 이상 입력을 받지 않아 무한루프에 들어가버린 것이다.

이를 방지하기 위한 방법은 간단하다.

int main() {
    int n;
    while (std::cin >> n) {
        std::cout << "input :: " << n << std::endl;
        if (n == 0) break;
    }
}

무한 루프에 빠지지 않고 break되서 나올 수 있다.

failbit가 이처럼 0으로 평가되는 이유는 cin에서 알 수 있는데, cin은 boolean context다. 즉 데이터를 성공적으로 처리하면 true, 실패하거나 EOF에 다다르게 되면 false를 반환한다는 것이다.

때문에 false를 의미하는 0이 되었을때 if문에 들어갈 수 있다는 것이다.

덧붙여서 failbit의 경우에도, 계속해서 다음 입력데이터를 받고 싶다면 .fail, .clear .ignore 를 사용하면 되나, 여기서 다루진 않으려고 한다.

File Read and Write

int main() {
  // 파일 읽기 준비
  std::ifstream in("test.txt");
  std::string s;

  if (in.is_open()) {
    in.seekg(0, std::ios::end);
    int size = in.tellg();//사이즈 확인
    s.resize(size);
    in.seekg(0, std::ios::beg);
    in.read(&s[0], size);
    std::cout << s << std::endl;
  }

  return 0;
}

골자는 간단하다. c에서도 그랬지만, c++역시 파일을 읽기 위해서는 위치지정자를 이동시켜줘야 한다. 핵심인 read()함수를 보면 인자를 두개 받는다. 각각 저장할 공간과 size다.

때문에 위치지정자를 두번 이동시킨다. 처음은 end로 이동시켜 사이즈 확인을, 두번째는 처음으로 이동시켜 read를 할 수 있게끔 한다.

개행문자(엔터)가 나올때까지 입력을 받는 getline을 사용하면 좀 더 간단해진다.

 while (in) {
    in.getline(buf, 100);
    std::cout << buf << std::endl;
  }

다음은 파일에 쓰기이다. 이 역시 어렵지 않다.

int main() {
  std::ofstream out("test.txt");
//out결과를 내야하기에 ofstream을 사용한다.
  std::string s;
  if (out.is_open()) {
    out << "write this!<<std::endl;
  }

  return 0;
}

cin처럼 방향이 <<임에 주의하자.

profile
공부 정리 노트

0개의 댓글