본문 바로가기
C++

[C++] 입력(Input)

by imagineer_jinny 2022. 1. 21.

본 내용은 POCU COMP3200: C++ 언매니지드 프로그래밍 강의를 토대로 작성하였습니다.

 

1. 입력 스트림(Input Stream)

 

  • 키보드에서 읽기
     
  • scanf는 왜 위험한가?
    • scanf()는 경계검사를 하지 않는다!
    • 경계검사 : 내가 사용하는, 쓸 메모리가 실제 내가 가진 메모리 내부에 들어가니? 들지 않으면 안전하게 연산을 하겠다. exception 등을 던진다.   
      C 스타일의 문자열은 Character Array, 바꿔 말하면 Character Pointer임.  그래서 어느 정도의 메모리가 할당되어 있는지 실행 중에는 알 수 없음. 그래서 string 제일 뒤에 특수한 Character을 넣어서 string이 끝나는 것을 표시함. 이게 NULL Character Stream.
    • 위 예제에서 문자 끝에 NULL 캐릭터가 있어야 하는데 NULL 캐릭터를 넣으려 하니까 빨간색 부분이 우리가 소유하지 않은 메모리임. 따라서 우리가 소유하지 않은 메모리를 덮어 쓰는 문제가 생김.
    • scanf는 char*를 받음. character array는 char pointer. char*를 받는데 그것의 길이를 알 수 없으니 무조건 읽음. 이걸 해결하려면?
      5개로 잡아주면 됨. 하지만 이게 범용적인 솔루션은 아님. 다른 이름이 긴 글자가 들어간다면?
    • cin은 같은 문제가 있을까?
      • 똑같은 문제가 있음. 이유 ? char 배열과 char 포인터는 같음
      • C++에서 쓸 수 있는 더 안전한 방법 - setw()
        내가 가진 버퍼 사이즈가 4글자 밖에 없으니 알아서 읽어오라고 하면 세 글자만 읽어오고 NULL 캐릭터를 붙여줌

 

 

2. 스트림 상태 (Stream States)

  • cin, cout은 스트림. 이 스트림에서 뭔가를 읽어 왔는데 가끔은 읽어올 때 실패할 때가 있을 것. 
  • 그럼 실패 했다는 것을 어떻게 확인? 성공, 실패 여부 찾기 위해 상태가 자동적으로 저장됨
    file의 마지막(end-of-file)을 만나지 않았으면 진행해라 / NULL은 직관적이지 않음

 

1) 456까지 읽고 a에서 멈춤. 456까지 무사히 읽었기 때문에 eof는 아니고(a가 eof가 아니니까) 실제 숫자 읽었으니 failbit set가 안됨

 

2) 456 넣었을 때 제대로 읽고(failbit == unset) 커서가 6 다음에 와있는건데 set이 될 수도 있고 안될수도 있음

  • 언제 set이 안돼?
    • 콘솔에서 데이터 입력할 때 456하고 엔터 치면 뒤에 뉴라인 캐릭터가 있기 때문에 무사히 읽고 뉴라인 캐릭터는 eof가 아니기 때문에 unset.
  • 언제 set이 됨?
    • input을 콘솔에서 하는 게 아니라 텍스트파일로 저장해두고 콘솔에서 치는 것 처럼 리디렉션을 할 수 있음.
    • 이렇게 하면 456뒤에 엔터 안치고 저장할 수 있어서 마지막 캐릭터가 eof가 되기 때문에 set이 됨
  • 그래서 eof 처리하는 코드 쓸 때 이 두가지 경우를 모두 테스트 해봐야 함.

 

3) abc를 숫자로 읽을 방법은 없으니 failbit가 set이 되고 실패를 하고 아직도 포인터는 a를 가리키고 있음. 따라서 eofbit은 set이 안됨

 

4) eof를 넣었을 땐 eof니까 set 되고 숫자가 아니니까 failbit도 set

//알파벳 e o f가 아니라 eof 입력하는 방법 있음. 구글에 쳐보기

 

 

 

3. 입력 버리기(Discarding Input) : clear(), ignore()

  • clear() 
    • 스트림을 좋은 상태(good state)로 돌려 줌
cin.clear();
  • 언제 써?
    • 스트림이 좋지 않은 상태가 되었을 때. failbit이 들어왔다거나 eof가 들어왔을 때 다시 스트림을 아무 문제 없는 상태(문제 있는 bit가 설정되었을 때 그걸 그냥 지워줄 때)로 만들어줌
    • 보통 대응 다 하고 스트림 상태 좋게 만들고 시도해볼 때 씀

 

  • ignore()
    • 아래 예제들은 파일 끝에 도달하거나 지정한 수 만큼 문자를 버리면 멈춤
cin.ignore(); //문자 1개를 버림
cin.ignore(10); //문자 10개를 버림
    • 언제 써?
      • abc를 숫자로 읽으려 했는데 못 읽은 경우 포인터가 계속 a에 머무르니까 그걸 건너뛰고 싶을 때 씀.

 

  • 문자 10개를 버림. 단, 그 전에 뉴라인(new line) 문자를 버리면 곧바로 멈춤
cin.ignore(10,'\n');

 

  • 최대 문자 수를 버림. 단, 그 전에 뉴라인 문자를 버리면 곧바로 멈춤
cin.ignore(LLONG_MAX,'\n');​

 

4. 입력 버리기 : get(), getline()

  • 둘다 한줄 읽어오는데 입력 스트림을 남기냐 버리냐 차이

 

  • get()
    • 뉴라인 문자를 만나기 직전까지의 모든 문자를 가져옴
    • 뉴라인 문자는 입력 스트림에 남아 있음
  • 99개 문자를 가져오거나 뉴라인 문자가 나올 때 까지의 문자를 가져오고, 가져온 문자들을 char 배열(firstName)에 배치함 
get(firstName, 100);
  • 99개 문자를 가져오거나 '#' 문자가 나올 때 까지의 문자를 가져오고, 가져온 문자들을 char 배열(firstName)에 배치함
get(firstName, 100, '#');​

 

  • getline()
    • 뉴라인 문자를 만나기 직전까지의 모든 문자를 가져옴
    • 뉴라인 문자는 입력 스트림에서 버림
getline(firstname,100);
getline(firstname,100,'#');

 

코드 샘플 : 정수의 합 구하기

#include "AddIntegers.h"
#include <iostream>

using namespace std;

namespace samples
{
	void AddIntegersExample()
	{
		cout << "+------------------------------+" << endl;
		cout << "|     Add Integers Example     |" << endl;
		cout << "+------------------------------+" << endl;

		int number;
		int sum = 0;

		while (true)
		{
			cout << "Please enter an integer or EOF: ";
			cin >> number;
			if (cin.eof())
			{
				break;
			}

			if (cin.fail())
			{
				cout << "Invalid input" << endl;
				cin.clear();
				cin.ignore(LLONG_MAX, '\n');
				continue;
			}
			sum += number;
		}
		cin.clear();

		cout << "The sum is " << sum << endl;
	}
}

 

코드 샘플 : getline() / 입력된 문자열 뒤집기 (면접 문제 많이 나옴 기본임)

#include "ReverseInputString.h"
#include <iostream>

using namespace std;

namespace samples
{
	void ReverseInputStringExample()
	{
		cout << "+------------------------------+" << endl;
		cout << "|    Reverse String Example    |" << endl;
		cout << "+------------------------------+" << endl;

		const int LINE_SIZE = 512;
		char line[LINE_SIZE];

		cout << "Please enter a string to reverse" << endl
			<< "or EOF to quit: ";

		cin.getline(line, LINE_SIZE);
		if (cin.fail())
		{
			cin.clear();
			return;
		}

		char* p = line;
		char* q = line + strlen(line) - 1;
		while (p < q)
		{
			char temp = *p;
			*p = *q;
			*q = temp;

			++p; //포인터 증가
			--q; //포인터 감소
		}
		
		cout << "Reversed string: " << line << endl;
	}
}

'C++' 카테고리의 다른 글

[C++] 참조(Reference)  (0) 2022.01.22
[C++] Bool 데이터형  (0) 2022.01.21
[C++] C 스타일 배열과 std::array  (0) 2022.01.20
[C++] 출력(Output)  (0) 2022.01.04
Overriding, Virtual 함수  (0) 2021.09.14

댓글