C++で非常に基本的なプログラムを作成し、ユーザーに数字と文字列の入力を求めました。驚いたことに、プログラムを実行するとき、文字列を要求するために停止することはありませんでした。スキップしました。 StackOverflowを読んだ後、次のような行を追加する必要があることがわかりました。
cin.ignore(256, '\n');
文字列入力を取得する行の前。これを追加することで問題が修正され、プログラムが機能するようになりました。私の質問は、C++がこのcin.ignore()
行を必要とする理由と、cin.ignore()
を使用する必要がある時期を予測する方法です。
私が書いたプログラムは次のとおりです。
#include <iostream>
#include <string>
using namespace std;
int main()
{
double num;
string mystr;
cout << "Please enter a number: " << "\n";
cin >> num;
cout << "Your number is: " << num << "\n";
cin.ignore(256, '\n'); // Why do I need this line?
cout << "Please enter your name: \n";
getline (cin, mystr);
cout << "So your name is " << mystr << "?\n";
cout << "Have a Nice day. \n";
}
無視はまさにその名前が意味するものです。
代わりに、必要のないものを「捨てる」のではなく、呼び出し時に指定する文字数を、ブレークポイントとして指定する文字まで無視します。
入力バッファと出力バッファの両方で機能します。
基本的に、std::cin
ステートメントでは、getline
呼び出しを行う前に無視を使用します。ユーザーがstd::cin
で何かを入力すると、Enterキーを押して'\n'
charがcin
バッファー。次に、getline
を使用すると、必要な文字列の代わりに改行文字が取得されます。 std::cin.ignore(1000,'\n')
を実行すると、必要な文字列までバッファがクリアされます。 (1000は、指定されたブレークポイント、この場合は\ n改行文字の前の特定の量の文字をスキップするためにそこに置かれます。)
あなたはこれについて間違った方法で考えています。 cin
またはgetline
が使用されるたびに、論理的なステップで考えています。例最初に番号を尋ね、次に名前を尋ねます。それはcin
について考える間違った方法です。したがって、入力を要求するたびにストリームがクリアであると想定するため、競合状態に陥ります。
入力専用のプログラムを作成すると、問題が見つかります。
void main(void)
{
double num;
string mystr;
cin >> num;
getline(cin, mystr);
cout << "num=" << num << ",mystr=\'" << mystr << "\'" << endl;
}
上記では、「最初に数字を取得する」と考えています。したがって、123
と入力してEnterキーを押すと、出力はnum=123,mystr=''
になります。何故ですか?これは、ストリームに123\n
があり、123
がまだストリームにある間に\n
がnum
変数に解析されるためです。デフォルトでgetline
関数のドキュメントを読むと、\n
が見つかるまでistream
を探します。この例では、\n
がストリームにあるため、「スキップ」されたように見えますが、正常に機能しました。
上記を機能させるには、123Hello World
を入力する必要があります。これにより、num=123,mystr='Hello World'
が適切に出力されます。または、cin
とgetline
の間にcin.ignore
を挿入して、予想される論理的なステップに分割するようにします。
これがignore
コマンドが必要な理由です。ストリーム形式ではなく論理ステップで考えているため、競合状態に陥ります。
学校で一般的に見られる別のコード例を取り上げます。
void main(void)
{
int age;
string firstName;
string lastName;
cout << "First name: ";
cin >> firstName;
cout << "Last name: ";
cin >> lastName;
cout << "Age: ";
cin >> age;
cout << "Hello " << firstName << " " << lastName << "! You are " << age << " years old!" << endl;
}
上記は論理的な段階にあるようです。最初に名、姓、年齢を尋ねます。したがって、John
を入力してからDoe
を入力してから19
を入力すると、アプリケーションは各論理ステップを実行します。 「ストリーム」で考える場合、「名:」質問にJohn Doe 19
と入力するだけで、同様に機能し、残りの質問をスキップするように見えます。上記を論理ステップで機能させるには、質問の各論理ブレークの残りのストリームをignore
する必要があります。
プログラムの入力は、論理的なステップではなく「ストリーム」から読み取られることを忘れないでください。 cin
を呼び出すたびに、ストリームから読み取られます。ユーザーが間違った入力をすると、これはかなりバグの多いアプリケーションを作成します。たとえば、cin >> double
が期待される場所に文字を入力した場合、アプリケーションはかなり(一見)奇妙な出力を生成します。
入力ストリームから特定の数の文字を手動で破棄する場合。
非常に一般的な使用例は、これを使用して改行文字を安全に無視することです。これは、cinが次の入力行に移動する必要がある改行文字を残すことがあるためです。
簡単に言えば、ストリーム入力を処理する際の柔軟性を提供します。
短い答え
どうして?入力ストリームにはまだ空白(キャリッジリターン、タブ、スペース、改行)が残っているためです。
いつ?独自に機能しない関数を使用している場合、先頭の空白は無視されます。 Cinはデフォルトで先頭の空白を無視して削除しますが、getlineはそれ自体で先頭の空白を無視しません。
今、詳細な答えがあります。
コンソールに入力するすべてのものは、標準ストリームの標準入力から読み取られます。何かを入力する場合、たとえば256を入力してEnterキーを押すと、ストリームの内容は256\n
になります。 cinは256をピックアップしてストリームから削除し、\n
がストリームに残っています。次に、名前を入力すると、Raddicus
とします。ストリームの新しいコンテンツは\nRaddicus
です。
今ここにキャッチが来ます。 getlineを使用して行を読み取ろうとすると、3番目の引数として区切り文字が指定されていない場合、getlineはデフォルトで改行文字まで読み取り、ストリームから改行文字を削除します。したがって、改行を呼び出すと、getlineはストリームから\n
を読み取り、破棄し、ストリーム内にすでに改行があるため、getlineがスキップされるように見えるmystrで空の文字列を読み取ります(しかし、そうではありません)読むはずだったものをすでに読んでいるので、入力を要求しません。
さて、cin.ignoreはここでどのように役立ちますか?
cplusplus.com -からのドキュメントの抜粋によると
istream&ignore(streamsize n = 1、int delim = EOF);
入力シーケンスから文字を抽出し、n文字が抽出されるか、またはdelimと等しいと比較されるまで、それらを破棄します。
また、ファイルの終わりに達した場合、関数は文字の抽出を停止します。 (n文字を抽出するか、またはdelimを見つける前に)早まってこれに達した場合、関数はeofbitフラグを設定します。
したがって、cin.ignore(256, '\n');
は、デリミタ(ここでは\ n)に遭遇するまで最初の256文字またはすべての文字を無視します(ここでは\ nが最初の文字なので、\ nに遭遇するまで無視します) 。
参考までに、スキップする文字数が正確にわからない場合、getlineまたはcinを使用して文字列を読み取るためにストリームをクリアすることが唯一の目的である場合は、cin.ignore(numeric_limits<streamsize>::max(),'\n')
を使用する必要があります。
簡単な説明:ストリームの最大サイズに等しい文字を無視するか、 '\ n'に遭遇するまで、どちらの場合でも最初に発生します。
無視機能を使用して、入力ストリーム内の文字をスキップ(破棄/破棄)します。ファイルを無視は、ファイルistreamに関連付けられています。例:cin.ignore(120、 '/ n');特定の関数は、次の120文字をスキップするか、改行文字が読み取られるまで文字をスキップします。