형식화 된 추출 후 std :: getline ()이 입력을 건너 뛰는 이유는 무엇입니까?
사용자에게 이름과 상태를 묻는 다음 코드가 있습니다.
#include <iostream>
#include <string>
int main()
{
std::string name;
std::string state;
if (std::cin >> name && std::getline(std::cin, state))
{
std::cout << "Your name is " << name << " and you live in " << state;
}
}
내가 찾은 것은 이름이 성공적으로 추출되었지만 상태가 아니라는 것입니다. 다음은 입력 및 결과 출력입니다.
Input: "John" "New Hampshire" Output: "Your name is John and you live in "
상태 이름이 출력에서 생략 된 이유는 무엇입니까? 적절한 입력을 제공했지만 코드는이를 무시합니다. 왜 이런 일이 발생합니까?
왜 이런 일이 발생합니까?
이것은 사용자가 직접 제공 한 입력과 거의 관련이 없지만 기본 동작 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
where next_pointer
is the next character to be parsed. Since the character stored at the next position in the input sequence is the delimiter, std::getline()
will quietly discard that character, increment next_pointer
to the next available character, and stop input. This means that the rest of the characters that you have provided still remain in the buffer for the next I/O operation. You'll notice that if you perform another read from the line into state
, your extraction will yield the correct result as the last call to std::getline()
discarded the delimiter.
You may have noticed that you don't typically run into this problem when extracting with the formatted input operator (operator>>()
). This is because input streams use whitespace as delimiters for input and have the std::skipws
1 manipulator set on by default. Streams will discard the leading whitespace from the stream when beginning to perform formatted input.2
Unlike the formatted input operators, std::getline()
is an unformatted input function. And all unformatted input functions have the following code somewhat in common:
typename std::basic_istream<charT>::sentry ok(istream_object, true);
The above is a sentry object which is instantiated in all formatted/unformatted I/O functions in a standard C++ implementation. Sentry objects are used for preparing the stream for I/O and determining whether or not it is in a fail state. You'll only find that in the unformatted input functions, the second argument to the sentry constructor is true
. That argument means that leading whitespace will not be discarded from the beginning of the input sequence. Here is the relevant quote from the Standard [§27.7.2.1.3/2]:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
[...] If
noskipws
is zero andis.flags() & ios_base::skipws
is nonzero, the function extracts and discards each character as long as the next available input characterc
is a whitespace character. [...]
Since the above condition is false, the sentry object will not discard the whitespace. The reason noskipws
is set to true
by this function is because the point of std::getline()
is to read raw, unformatted characters into a std::basic_string<charT>
object.
The Solution:
There's no way to stop this behavior of std::getline()
. What you'll have to do is discard the new line yourself before std::getline()
runs (but do it after the formatted extraction). This can be done by using ignore()
to discard the rest of the input until we reach a fresh new line:
if (std::cin >> name &&
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') &&
std::getline(std::cin, state))
{ ... }
You'll need to include <limits>
to use std::numeric_limits
. std::basic_istream<...>::ignore()
is a function that discards a specified amount of characters until it either finds a delimiter or reaches the end of the stream (ignore()
also discards the delimiter if it finds it). The max()
function returns the largest amount of characters that a stream can accept.
Another way to discard the whitespace is to use the std::ws
function which is a manipulator designed to extract and discard leading whitespace from the beginning of an input stream:
if (std::cin >> name && std::getline(std::cin >> std::ws, state))
{ ... }
What's the difference?
The difference is that ignore(std::streamsize count = 1, int_type delim = Traits::eof())
3 indiscriminately discards characters until it either discards count
characters, finds the delimiter (specified by the second argument delim
) or hits the end of the stream. std::ws
is only used for discarding whitespace characters from the beginning of the stream.
If you are mixing formatted input with unformatted input and you need to discard residual whitespace, use std::ws
. Otherwise, if you need to clear out invalid input regardless of what it is, use ignore()
. In our example, we only need to clear whitespace since the stream consumed your input of "John"
for the name
variable. All that was left was the newline character.
1: std::skipws
is manipulator that tells the input stream to discard leading whitespace when performing formatted input. This can be turned off with the std::noskipws
manipulator.
2: Input streams deem certain characters as whitespace by default, such the space character, newline character, form feed, carriage return, etc.
3: This is the signature of std::basic_istream<...>::ignore()
. You can call it with zero arguments to discard a single character from the stream, one argument to discard a certain amount of characters, or two arguments to discard count
characters or until it reaches delim
, whichever one comes first. You normally use std::numeric_limits<std::streamsize>::max()
as the value of count
if you don't know how many characters there are before the delimiter, but you want to discard them anyway.
Everything will be OK if you change your initial code in the following way:
if ((cin >> name).get() && std::getline(cin, state))
This happens because an implicit line feed also known as newline character \n
is appended to all user input from a terminal as it's telling the stream to start a new line. You can safely account for this by using std::getline
when checking for multiple lines of user input. The default behavior of std::getline
will read everything up to and including the newline character \n
from the input stream object which is std::cin
in this case.
#include <iostream>
#include <string>
int main()
{
std::string name;
std::string state;
if (std::getline(std::cin, name) && std::getline(std::cin, state))
{
std::cout << "Your name is " << name << " and you live in " << state;
}
return 0;
}
Input: "John" "New Hampshire" Output: "Your name is John and you live in New Hampshire"
'IT박스' 카테고리의 다른 글
swf 파일을 어떻게 디 컴파일합니까 [closed] (0) | 2020.09.06 |
---|---|
x64 Java에서 int보다 긴 이유는 무엇입니까? (0) | 2020.09.06 |
J2ME / Android / BlackBerry-운전 경로, 두 위치 간 경로 (0) | 2020.09.06 |
C (또는 일반적으로 절차 적 프로그래밍)를위한 디자인 원칙, 모범 사례 및 디자인 패턴? (0) | 2020.09.06 |
CSS3 : 검사되지 않은 의사 클래스 (0) | 2020.09.06 |