なぜこれが起こるのですか?
これは、自分で提供した入力とはほとんど関係ありませんが、デフォルトの動作がstd::getline()
示しています。名前(std::cin >> name
)を入力すると、次の文字だけでなく、暗黙の改行もストリームに追加されます。
"John\n"
端末から選択EnterまたはReturn送信するときは、常に改行が入力に追加されます。次の行に移動するためのファイルでも使用されます。改行は、抽出後、name
破棄または消費される次のI / O操作まで、バッファーに残されます。制御のフローがに達するstd::getline()
と、改行は破棄されますが、入力はすぐに停止します。これが発生する理由は、この関数のデフォルトの機能が必要であることを指示しているためです(行を読み取ろうとし、改行が見つかると停止します)。
この先頭の改行はプログラムの期待される機能を阻害するため、無視されたものをスキップする必要があります。1つのオプションは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)文字が見つかった場合
- 区切り文字が見つかった場合
3番目の条件は、私たちが扱っているものです。への入力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のためにストリームを準備し、それが障害状態にあるかどうかを判断するために使用されます。フォーマットされていない入力関数では、セントリーコンストラクターの2番目の引数はであることがわかりますtrue
。その引数は、先頭の空白が入力シーケンスの先頭から破棄されないことを意味します。標準からの関連する引用は次のとおりです[§27.7.2.1.3/ 2]:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
[...] noskipws
がゼロでis.flags() & ios_base::skipws
非ゼロの場合、関数は、次に使用可能な入力文字c
が空白文字である限り、各文字を抽出して破棄します。[...]
上記の条件はfalseであるため、監視オブジェクトは空白を破棄しません。この関数によって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
、区切り文字(2番目の引数で指定delim
)を見つけるか、ストリームの終わりに到達するまで、無差別に文字を破棄することです。std::ws
ストリームの最初から空白文字を破棄するためにのみ使用されます。
フォーマットされた入力とフォーマットされていない入力を混在させており、残りの空白を破棄する必要がある場合は、を使用しますstd::ws
。それ以外の場合に、無効な入力をクリアする必要がある場合は、を使用してくださいignore()
。この例では、ストリーム"John"
がname
変数の入力を消費したため、空白をクリアするだけで済みます。残ったのは改行文字だけでした。
1:std::skipws
フォーマットされた入力を実行するときに、入力ストリームに先頭の空白を破棄するように指示するマニピュレータです。これはstd::noskipws
マニピュレータでオフにすることができます。
2:入力ストリームは、デフォルトで、空白文字、改行文字、フォームフィード、キャリッジリターンなどの特定の文字を空白と見なします。
3:これはの署名ですstd::basic_istream<...>::ignore()
。引数を0として呼び出して、ストリームから1文字を破棄し、1つの引数で特定の数の文字を破棄するか、2つの引数で文字を破棄するか、またはcount
に到達するまでのdelim
いずれか早い方で呼び出します。区切り文字の前に何文字あるかわからないが、とにかくそれらを破棄したい場合std::numeric_limits<std::streamsize>::max()
は、通常、の値として使用count
します。
std::cin >> name && std::cin >> std::skipws && std::getline(std::cin, state)
期待通りにも動作するはずです。(以下の回答に加えて)。