Giriş okuduktan sonra neden cin.clear () ve cin.ignore () 'u çağıralım?


86

Google Code University'nin C ++ öğreticisi , bu koda sahipti:

// Description: Illustrate the use of cin to get input
// and how to recover from errors.

#include <iostream>
using namespace std;

int main()
{
  int input_var = 0;
  // Enter the do while loop and stay there until either
  // a non-numeric is entered, or -1 is entered.  Note that
  // cin will accept any integer, 4, 40, 400, etc.
  do {
    cout << "Enter a number (-1 = quit): ";
    // The following line accepts input from the keyboard into
    // variable input_var.
    // cin returns false if an input operation fails, that is, if
    // something other than an int (the type of input_var) is entered.
    if (!(cin >> input_var)) {
      cout << "Please enter numbers only." << endl;
      cin.clear();
      cin.ignore(10000,'\n');
    }
    if (input_var != -1) {
      cout << "You entered " << input_var << endl;
    }
  }
  while (input_var != -1);
  cout << "All done." << endl;

  return 0;
}

Önemi nedir cin.clear()ve cin.ignore()? 10000Ve \nparametreleri neden gereklidir?


2
Bu, tüm zamanların en çok oy alan görevidir. Lisede zirveye çıkmış olmalıyım.
JacKeown

Yanıtlar:


104

cin.clear()Hata işaretini temizler cin(o gelecek G / Ç işlemleri doğru şekilde çalışır böylece) ve ardından cin.ignore(10000, '\n')bir sonraki yeni satır atlar (non-numarasıyla aynı hat üzerinde başka bir şey yok saymak bu yüzden başka bir ayrıştırma hatası neden olmaması) . Yalnızca 10000 karaktere kadar atlayacaktır, bu nedenle kod, kullanıcının çok uzun, geçersiz bir satıra koymayacağını varsayar.


59
+1. 10000 karaktere kadar görmezden gelmek yerine, kullanmak daha iyi olacağını eklemek istiyorum cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');.
Dennis

31
Kullanmak istiyorsanız std::numeric_limits, emin olun #include <limits>.
gbmhunter

1
Birisi sözdizimini açıklayabilir std::numeric_limits<std::streamsize>::max()mi?
Minh Tran

4
@Minh Tran: std :: streamsize, aktarılan G / Ç karakterinin sayısını veya G / Ç arabelleğinin boyutunu veren işaretli integraldir. Burada, "numeric_limits" şablon sınıfını kullanarak, G / Ç arabelleğinin veya aktarılan karakterin maksimum sınırını bilmek istedik.
Pankaj

1
@Minh Tran: sadece bir küçük düzeltme "akış boyutu" bir sınıf değil, sadece bir integral tipidir. Başkalarının limitini de elde edebiliriz yani. int, char vb .. lütfen [link] 'den kontrol edin ( en.cppreference.com/w/cpp/types/numeric_limits )
Pankaj

47

Giriyorsun

if (!(cin >> input_var))

cin'den girdi alırken bir hata oluşursa ifadesi. Bir hata meydana gelirse, bir hata bayrağı ayarlanır ve gelecekteki girdi alma girişimleri başarısız olur. Bu yüzden ihtiyacın var

cin.clear();

hata bayrağından kurtulmak için. Ayrıca, başarısız olan girdi, varsaydığım gibi bir tür tamponda oturuyor olacak. Tekrar giriş almaya çalıştığınızda, aynı girişi tamponda okuyacak ve tekrar başarısız olacaktır. Bu yüzden ihtiyacın var

cin.ignore(10000,'\n');

Arabellekten 10000 karakter alır ancak bir satırsonu (\ n) ile karşılaşırsa durur. 10000, yalnızca genel bir büyük değerdir.


11
Yoksay için varsayılanın tek bir karakteri atlamak olduğunu ekleyerek, tüm satırı atlamak için daha büyük bir sayıya ihtiyacınız var.
Bo Persson

24

Neden kullanıyoruz:

1) cin.ignore

2) cin.clear

?

Basitçe:

1) Akışta istemediğimiz değerleri yok saymak (ayıklamak ve atmak)

2) Akışın dahili durumunu temizlemek için. Cin.clear kullanıldıktan sonra dahili durumu tekrar iyi bit'e ayarlanır, bu da 'hata' olmadığı anlamına gelir.

Uzun versiyon:

Bir şey 'akış'a (cin) konursa, o zaman oradan alınmalıdır. "Alınan" derken "kullanılmış", "kaldırılmış", "çıkarılmış" diyoruz. Akışın bir akışı var. Veriler, akıştaki su gibi cin üzerinde akıyor. Su akışını durduramazsınız;)

Örneğe bakınız:

string name; //line 1
cout << "Give me your name and surname:"<<endl;//line 2
cin >> name;//line 3
int age;//line 4
cout << "Give me your age:" <<endl;//line 5
cin >> age;//line 6

Kullanıcı ilk soru için "Arkadiusz Wlodarczyk" yanıtını verirse ne olur?

Kendiniz görmek için programı çalıştırın.

Konsolda "Arkadiusz" göreceksiniz, ancak program sizden "yaş" ı sormayacaktır. "Arkadiusz" yazdırıldıktan hemen sonra bitecek.

Ve "Wlodarczyk" gösterilmiyor. Sanki gitmiş gibi görünüyor (?) *

Ne oldu? ;-)

Çünkü "Arkadiusz" ile "Wlodarczyk" arasında bir boşluk var.

İsim ve soyisim arasındaki "boşluk" karakteri, "giriş" akışında çıkarılmayı bekleyen iki değişken olduğunun bilgisayar için bir işaretidir.

Bilgisayar, birden fazla değişkeni girdiye göndermeye çalıştığınızı düşünüyor. Bu "boşluk" işareti, onu bu şekilde yorumlaması için bir işarettir.

Böylece bilgisayar "Arkadiusz" u "isim" (2) 'ye atar ve akışa (giriş) birden fazla dizge koyduğunuz için, bilgisayar "Wlodarczyk" değerini "age" (!) Değişkenine atamaya çalışır. Kullanıcının 6. satırdaki 'cin'e herhangi bir şey koyma şansı olmayacak çünkü bu talimat zaten yürütülmüştür (!). Neden? Çünkü akışta hala bir şey kalmıştı. Ve daha önce de söylediğim gibi, akış bir akış içindedir, bu yüzden her şey mümkün olan en kısa sürede ondan uzaklaştırılmalıdır. Ve olasılık, bilgisayar eğitimi gördüğü zaman geldi cin >> yaş;

Bilgisayar, birinin yaşını saklayan bir değişken oluşturduğunuzu bilmiyor (4. satır). "yaş" yalnızca bir etikettir. Bilgisayar için "yaş" da "afsfasgfsagasggas" olarak adlandırılabilir ve aynı olur. Onun için bu sadece bir değişkendir ki "Wlodarczyk" atamaya çalışacak çünkü siz bilgisayarı 6. satırda bunu yapmasını emrettiniz / talimat verdiniz.

Bunu yapmak yanlış, ama hey bunu yapan sensin! Bu senin hatan! Belki kullanıcı, ama yine de ...


Tamam tamam. Ama nasıl düzeltilir ?!

Birkaç ilginç şey daha öğrenmek için düzgün bir şekilde düzeltmeden önce bu örnekle biraz oynamaya çalışalım :-)

Bir şeyleri anladığımız yerde bir yaklaşım yapmayı tercih ederim. Nasıl yaptığımızı bilmeden bir şeyi tamir etmek tatmin olmaz, sence de öyle değil mi? :)

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate(); //new line is here :-)

Yukarıdaki kodu çağırdıktan sonra akışınızın (cin) durumunun 4'e (satır 7) eşit olduğunu fark edeceksiniz. Bu, iç durumunun artık iyi bitime eşit olmadığı anlamına gelir. Bir şeyler ters gitti. Oldukça açık, değil mi? Dize türü değerini ("Wlodarczyk") int türü değişken 'age'e atamayı denediniz. Türler eşleşmiyor. Bir şeylerin yanlış olduğunu bildirmenin zamanı geldi. Ve bilgisayar bunu, akışın iç durumunu değiştirerek yapar. Şöyle: "Seni sersem adamım, düzelt beni lütfen. Seni 'nazikçe' ;-)"

Artık 'cin' (akış) kullanamazsınız. Sıkışmış. Sanki su akıntısına büyük tahta kütükler koymuşsunuz gibi. Kullanmadan önce tamir etmelisiniz. Veri (su) artık o akıştan (cin) elde edilemez çünkü odun kütüğü (iç durum) bunu yapmanıza izin vermez.

Öyleyse, bir engel varsa (tahta kütükler), bunu yapmak için yapılmış araçları kullanarak onu kaldırabilir miyiz?

Evet!

Cin'in 4'e ayarlanmış dahili durumu, uluyan ve gürültü çıkaran bir alarma benzer.

cin.clear, durumu normale döndürür (iyi bit). Sanki gelip alarmı susturmuşsun gibi. Sadece erteledin. Bir şey olduğunu biliyorsunuz ve şöyle diyorsunuz: "Gürültü yapmayı bırakmak sorun değil, zaten bir şeylerin yanlış olduğunu biliyorum, kapa çeneni (net)".

Pekala, hadi yapalım! Cin.clear () kullanalım.

İlk girdi olarak "Arkadiusz Wlodarczyk" kullanarak aşağıdaki kodu çağırın:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl; 
cin.clear(); //new line is here :-)
cout << cin.rdstate()<< endl;  //new line is here :-)

Yukarıdaki kodu çalıştırdıktan sonra durumun goodbit'e eşit olduğunu kesinlikle görebiliriz.

Harika, sorun çözüldü mü?

İlk girdi olarak "Arkadiusz Wlodarczyk" kullanarak aşağıdaki kodu çağırın:

string name;
cout << "Give me your name and surname:"<<endl;
cin >> name;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << cin.rdstate() << endl;; 
cin.clear(); 
cout << cin.rdstate() << endl; 
cin >> age;//new line is here :-)

Durum, 9. satırdan sonra iyi bit olarak ayarlanmış olsa bile, kullanıcıdan "yaş" istenmez. Program durur.

NEDEN?!

Ah adamım ... Az önce alarmı bıraktın, peki ya bir suyun içindeki odun kütüğü? * "Wlodarczyk" in sözde nasıl gittiğinden bahsettiğimiz metne geri dön.

O odun parçasını dereden "Wlodarczyk" çıkarmalısın. Alarmları kapatmak sorunu hiç çözmez. Az önce susturdunuz ve sorunun çözüldüğünü mü düşünüyorsunuz? ;)

Yani başka bir aracın zamanı geldi:

cin.ignore, akıntıya sıkışan ahşap kütükleri gelen ve kaldıran halatlı özel bir kamyona benzetilebilir. Programınızın kullanıcısının yarattığı problemi temizler.

Yani alarmı çalmadan önce kullanabilir miyiz?

Evet:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;

"Wlodarczyk" 7. satırda gürültü yapmadan önce kaldırılacak.

10000 ve '\ n' nedir?

10000 karakteri (her ihtimale karşı) '\ n' karşılanana kadar (ENTER) kaldırın diyor. BTW sayısal_sınırlar kullanılarak daha iyi yapılabilir, ancak bu cevabın konusu bu değildir.


Yani sorunun ana nedeni gürültü yapılmadan önce ortadan kalktı ...

O halde neden 'temizliğe' ihtiyacımız var?

Ya birisi 6. satırda 'bana yaşını ver' sorusunu sorduysa, örneğin: 20 yazmak yerine "yirmi yaşında" sorusu?

Türler yine eşleşmiyor. Bilgisayar int'e dizge atamaya çalışır. Ve alarm başlar. Böyle bir duruma tepki verme şansın bile yok. cin.ignore böyle bir durumda size yardımcı olmayacaktır.

Öyleyse böyle durumlarda net kullanmalıyız:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
int age;
cout << "Give me your age:" << endl;
cin >> age;
cin.clear();
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

Ama durumu 'her ihtimale karşı' temizlemelisiniz?

Tabii ki değil.

Bir şeyler ters giderse (cin >> yaş;) talimatı yanlış döndürerek sizi bilgilendirecektir.

Böylece, kullanıcının akışa yanlış tür yazıp yazmadığını kontrol etmek için koşullu ifadeyi kullanabiliriz

int age;
if (cin >> age) //it's gonna return false if types doesn't match
    cout << "You put integer";
else
    cout << "You bad boy! it was supposed to be int";

Pekala, ilk sorunumuzu şu şekilde çözebiliriz:

string name;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow

int age;
cout << "Give me your age:" << endl;
if (cin >> age)
  cout << "Your age is equal to:" << endl;
else
{
 cin.clear();
 cin.ignore(10000, '\n'); //time to remove "Wlodarczyk" the wood log and make the stream flow
 cout << "Give me your age name as string I dare you";
 cin >> age;
}

Elbette bu, örneğin döngü while kullanarak söz konusu yaptığınız şeyi yaparak geliştirilebilir.

BONUS:

Merak ediyor olabilirsiniz. Kullanıcıdan aynı satırda ad ve soyad almak istersem ne olur? Cin, "boşluk" ile ayrılmış her değeri farklı değişken olarak yorumlarsa, cin kullanmak mümkün müdür?

Elbette, bunu iki şekilde yapabilirsiniz:

1)

string name, surname;
cout << "Give me your name and surname:"<< endl;
cin >> name;
cin >> surname;

cout << "Hello, " << name << " " << surname << endl;

2) veya getline işlevini kullanarak.

getline(cin, nameOfStringVariable);

ve bu nasıl yapılır:

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

İkinci seçenek, getline'dan önce 'cin' kullandıktan sonra kullanmanız durumunda sizi geri tepebilir.

Hadi kontrol edelim:

a)

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endl;

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Yaş olarak "20" koyarsanız adınız ve soyadınız istenmez.

Ama bu şekilde yaparsanız:

b)

string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;
int age;
cout << "Give me your age:" <<endl;
cin >> age;
cout << "Your age is" << age << endll

herşey yolunda.

NE?!

Girişe (akışa) bir şey koyduğunuzda, sonunda beyaz karakter olan ENTER ('\ n') bırakırsınız. Konsola bir şekilde değerler girmeniz gerekir. Yani veriler kullanıcıdan geliyorsa gerçekleşmelidir.

b) cin özellikleri, boşlukları yok saymasıdır, bu nedenle cin'den bilgi okurken, satırsonu karakteri '\ n' önemli değildir. Görmezden gelinir.

a) getline işlevi, satırsonu karakterine ('\ n') kadar olan tüm satırı alır ve satırsonu karakteri ilk şey olduğunda getline işlevi '\ n' alır ve elde edilecek tek şey budur. 3. satırda akışa "20" koyan kullanıcı tarafından akışta bırakılan yeni satır karakterini çıkarırsınız.

Yani onu düzeltmek için her zaman cin.ignore () 'u çağırmaktır; programınızın içinde getline () kullanacaksanız, herhangi bir değer elde etmek için cin'i her kullandığınızda.

Dolayısıyla doğru kod şu olacaktır:

int age;
cout << "Give me your age:" <<endl;
cin >> age;
cin.ignore(); // it ignores just enter without arguments being sent. it's same as cin.ignore(1, '\n') 
cout << "Your age is" << age << endl;


string nameAndSurname;
cout << "Give me your name and surname:"<< endl;
getline(cin, nameAndSurname);

cout << "Hello, " << nameAndSurname << endl;

Umarım akışlar sizin için daha nettir.

Hah sustur beni lütfen! :-)


1

tampondaki cin.ignore(1000,'\n')öncekinin tüm karakterlerini temizlemek için kullanın cin.get()ve "\ n" ile karşılaştığında veya 1000 charsilk karşılaştığında durmayı seçecektir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.