왜 이런 일이 발생합니까?
이것은 사용자가 직접 제공 한 입력과 거의 관련이 없지만 기본 동작 std::getline()
이 나타내는 것과 관련 이 있습니다. 이름 ( std::cin >> name
)에 대한 입력을 제공 할 때 다음 문자를 제출했을뿐만 아니라 암시 적 개행이 스트림에 추가되었습니다.
"John\n"
선택 Enter하거나 Return터미널에서 제출할 때 항상 줄 바꿈이 입력에 추가됩니다 . 다음 줄로 이동하기 위해 파일에서도 사용됩니다. 줄 바꿈은 추출 후 name
다음 I / O 작업까지 버퍼에 남아 있습니다. 여기서 버려지거나 소비됩니다. 제어 흐름이에 도달 std::getline()
하면 개행 문자가 삭제되지만 입력은 즉시 중지됩니다. 이런 일이 발생하는 이유는이 함수의 기본 기능이해야한다고 지시하기 때문입니다 (행을 읽으려고 시도하고 개행을 찾으면 중지).
이 줄 바꿈은 프로그램의 예상되는 기능을 방해하기 때문에 무시해야합니다. 한 가지 옵션은 std::cin.ignore()
첫 번째 추출 후 호출 하는 것입니다. 줄 바꿈이 더 이상 방해가되지 않도록 다음 사용 가능한 문자를 버립니다.
std::getline(std::cin.ignore(), state)
자세한 설명 :
이것은 std::getline()
당신이 부르는 과부하입니다 .
template<class charT>
std::basic_istream<charT>& getline( std::basic_istream<charT>& input,
std::basic_string<charT>& str )
이 함수의 또 다른 오버로드는 유형의 구분 기호를 사용합니다 charT
. 구분 문자는 입력 시퀀스 간의 경계를 나타내는 문자입니다. 이 특정 오버로드는 구분 기호가 input.widen('\n')
제공되지 않았기 때문에 기본적으로 개행 문자 로 설정합니다.
이제 다음은 std::getline()
입력 을 종료 하는 몇 가지 조건입니다 .
- 스트림이
std::basic_string<charT>
보유 할 수 있는 최대 문자 수를 추출한 경우
- EOF (파일 끝) 문자가 발견 된 경우
- 구분자가 발견 된 경우
세 번째 조건은 우리가 다루고있는 조건입니다. 에 대한 입력 state
은 다음과 같이 표시됩니다.
"John\nNew Hampshire"
^
|
next_pointer
next_pointer
구문 분석 할 다음 문자는 어디에 있습니까 ? 입력 시퀀스의 다음 위치에 저장된 문자가 구분 기호이므로 std::getline()
조용히 해당 문자를 버리고 next_pointer
사용 가능한 다음 문자로 증가 하고 입력을 중지합니다. 이는 제공 한 나머지 문자가 다음 I / O 작업을 위해 버퍼에 계속 남아 있음을 의미합니다. 행에서로 다른 읽기를 수행 하면 구분 기호 state
를 std::getline()
버리는 마지막 호출로 추출이 올바른 결과를 산출한다는 것을 알 수 있습니다 .
형식이 지정된 입력 연산자 ( operator>>()
)를 사용하여 추출 할 때 일반적으로이 문제가 발생하지 않는다는 것을 알 수 있습니다 . 이는 입력 스트림이 공백을 입력 구분 기호로 사용 하고 기본적으로 std::skipws
1 개의 조작자가 설정되어 있기 때문 입니다. 스트림은 형식화 된 입력을 수행하기 시작할 때 스트림에서 선행 공백을 삭제합니다. 2
포맷 된 입력 연산자 달리 std::getline()
이다 형식화 입력 기능. 그리고 모든 형식화되지 않은 입력 함수에는 다음과 같은 코드가 공통적으로 있습니다.
typename std::basic_istream<charT>::sentry ok(istream_object, true);
위는 표준 C ++ 구현에서 모든 형식화 / 형식화되지 않은 I / O 함수에서 인스턴스화되는 센트리 개체입니다. Sentry 개체는 I / O 용 스트림을 준비하고 실패 상태인지 여부를 결정하는 데 사용됩니다. 형식이 지정 되지 않은 입력 함수 에서만 센트리 생성자의 두 번째 인수가 true
. 이 인수 는 입력 시퀀스의 시작 부분에서 선행 공백이 삭제 되지 않음을 의미합니다 . 다음은 표준 [§27.7.2.1.3 / 2]의 관련 인용문입니다.
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
[...] noskipws
가 0이고 0 is.flags() & ios_base::skipws
이 아닌 경우 다음 사용 가능한 입력 문자 c
가 공백 문자 인 한 함수는 각 문자를 추출하여 버립니다 . [...]
위의 조건이 거짓이므로 센트리 객체는 공백을 버리지 않습니다. 이 함수 noskipws
에 true
의해 설정 되는 이유 는 요점이 std::getline()
형식화되지 않은 원시 문자를 std::basic_string<charT>
객체 로 읽어 들이기 때문 입니다.
해결책:
의이 동작을 중지 할 방법이 없습니다 std::getline()
. 해야 할 일은 std::getline()
실행 하기 전에 새 줄을 직접 버리는 것입니다 (그러나 형식화 된 추출 후에 수행하십시오 ). 이것은 ignore()
새로운 줄에 도달 할 때까지 나머지 입력을 버리기 위해 를 사용하여 수행 할 수 있습니다 .
if (std::cin >> name &&
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') &&
std::getline(std::cin, state))
{ ... }
<limits>
을 사용 하려면 포함 해야합니다 std::numeric_limits
. std::basic_istream<...>::ignore()
구분 기호를 찾거나 스트림의 끝에 도달 할 때까지 지정된 양의 문자를 버리는 함수입니다 (찾으면 구분 ignore()
기호도 버립니다). 이 max()
함수는 스트림이 허용 할 수있는 최대 문자 수를 반환합니다.
공백을 버리는 또 다른 방법 std::ws
은 입력 스트림의 시작 부분에서 선행 공백을 추출하고 삭제하도록 설계된 조작자 인 함수 를 사용하는 것입니다.
if (std::cin >> name && std::getline(std::cin >> std::ws, state))
{ ... }
차이점이 뭐야?
차이점은 ignore(std::streamsize count = 1, int_type delim = Traits::eof())
3 은 문자를 버리거나 count
구분 기호 (두 번째 인수로 지정됨 delim
)를 찾거나 스트림의 끝에 도달 할 때까지 무차별 적으로 문자를 버린다는 것 입니다. std::ws
스트림 시작 부분에서 공백 문자를 버릴 때만 사용됩니다.
형식이 지정된 입력을 형식이 지정되지 않은 입력과 혼합하고 나머지 공백을 버려야하는 경우 std::ws
. 그렇지 않으면 입력 내용에 관계없이 유효하지 않은 입력을 지워야하는 경우 ignore()
. 이 예에서는 스트림 "John"
이 name
변수 에 대한 입력을 소비했기 때문에 공백 만 지울 필요가 있습니다. 남은 것은 개행 문자뿐이었습니다.
1 : std::skipws
형식화 된 입력을 수행 할 때 선행 공백을 버리도록 입력 스트림에 지시하는 조작기입니다. std::noskipws
조작기 로 끌 수 있습니다 .
2 : 입력 스트림은 공백 문자, 개행 문자, 용지 공급, 캐리지 리턴 등과 같은 특정 문자를 기본적으로 공백으로 간주합니다.
3 : 이것은의 서명입니다 std::basic_istream<...>::ignore()
. 0 인수로 호출하여 스트림에서 단일 문자를 버리고, 하나의 인수를 사용하여 특정 양의 문자를 버리거나, 두 개의 인수를 사용하여 count
문자 를 버리거나에 도달 할 때까지 delim
둘 중 하나가 먼저 올 수 있습니다. 일반적으로 구분 기호 앞에 몇 개의 문자가 있는지 모르는 경우 std::numeric_limits<std::streamsize>::max()
값 으로 사용 count
하지만 어쨌든 버리고 싶을 때 사용합니다.
std::cin >> name && std::cin >> std::skipws && std::getline(std::cin, state)
예상대로 작동해야 한다고 생각 합니다. (아래 답변 외에도).