Tại sao chúng ta gọi cin.clear () và cin.ignore () sau khi đọc đầu vào?


86

Hướng dẫn C ++ của Google Code University từng có mã này:

// 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;
}

Ý nghĩa của cin.clear()và là cin.ignore()gì? Tại sao các 10000\ntham số lại cần thiết?


1
Đây là bài đăng được ủng hộ nhiều nhất mọi thời đại của tôi cho đến nay. Chắc hẳn tôi đã đạt đỉnh ở trường trung học.
JacKeown

Câu trả lời:


102

Các cin.clear()xóa cờ lỗi trên cin(vì vậy trong tương lai mà I / O hoạt động sẽ làm việc một cách chính xác), và sau đó cin.ignore(10000, '\n')bỏ qua để xuống dòng tiếp theo (bỏ qua bất cứ điều gì khác trên cùng một dòng như phi số để nó không gây ra một sự thất bại phân tích cú pháp) . Nó sẽ chỉ bỏ qua tối đa 10000 ký tự, vì vậy mã giả định người dùng sẽ không đặt vào một dòng quá dài, không hợp lệ.


57
+1. Tôi muốn thêm điều đó thay vì bỏ qua tối đa 10000 ký tự, tốt hơn là nên sử dụng cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');.
Dennis

30
Nếu bạn muốn sử dụng std::numeric_limits, hãy chắc chắn rằng #include <limits>.
gbmhunter

1
Ai đó có thể giải thích cú pháp std::numeric_limits<std::streamsize>::max()?
Minh Tran,

4
@Minh Tran: std :: Streamize là tích phân có dấu cho biết số ký tự i / o được chuyển hoặc kích thước của bộ đệm I / O. Ở đây, bằng cách sử dụng lớp mẫu "numeric_limits", chúng tôi muốn biết giới hạn tối đa của bộ đệm I / O hoặc ký tự được chuyển.
Pankaj

1
@Minh Tran: chỉ cần một chỉnh sửa nhỏ là "streamize" không phải là class, nó chỉ là kiểu tích phân. Chúng ta cũng có thể lấy được giới hạn của người khác. int, char vv .. xin vui lòng kiểm tra xem nó ở [link] ( en.cppreference.com/w/cpp/types/numeric_limits )
Pankaj

46

Bạn nhập

if (!(cin >> input_var))

nếu có lỗi xảy ra khi lấy đầu vào từ cin. Nếu lỗi xảy ra thì cờ lỗi được đặt và các nỗ lực lấy đầu vào trong tương lai sẽ không thành công. Đó là lý do tại sao bạn cần

cin.clear();

để loại bỏ cờ lỗi. Ngoài ra, đầu vào không thành công sẽ nằm trong những gì tôi giả định là một số loại bộ đệm. Khi bạn cố gắng lấy lại dữ liệu đầu vào, nó sẽ đọc cùng một đầu vào trong bộ đệm và nó sẽ bị lỗi một lần nữa. Đó là lý do tại sao bạn cần

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

Nó lấy ra 10000 ký tự từ bộ đệm nhưng sẽ dừng lại nếu nó gặp một dòng mới (\ n). 10000 chỉ là một giá trị lớn chung chung.


11
Chỉ cần thêm rằng mặc định cho bỏ qua là bỏ qua một ký tự, vì vậy bạn cần một số lớn hơn để bỏ qua toàn bộ dòng.
Bo Persson

23

Tại sao chúng tôi sử dụng:

1) cin.ignore

2) cin.clear

?

Đơn giản:

1) Bỏ qua (trích xuất và loại bỏ) các giá trị mà chúng tôi không muốn trên luồng

2) Để xóa trạng thái nội bộ của luồng. Sau khi sử dụng cin.clear, trạng thái bên trong của cin.clear được đặt trở lại goodbit, có nghĩa là không có 'lỗi' nào.

Phiên bản dài:

Nếu thứ gì đó được đưa vào 'stream' (cin) thì nó phải được lấy từ đó. Khi 'lấy', chúng tôi có nghĩa là 'đã sử dụng', 'loại bỏ', 'trích xuất' khỏi luồng. Suối có dòng chảy. Dữ liệu đang chảy trên cin như nước trên suối. Bạn chỉ đơn giản là không thể ngăn dòng chảy của nước;)

Nhìn vào ví dụ:

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

Điều gì xảy ra nếu người dùng trả lời: "Arkadiusz Wlodarczyk" cho câu hỏi đầu tiên?

Chạy chương trình để xem cho chính mình.

Bạn sẽ thấy trên bảng điều khiển "Arkadiusz" nhưng chương trình sẽ không hỏi bạn về 'tuổi'. Nó sẽ chỉ hoàn thành ngay sau khi in "Arkadiusz".

Và "Wlodarczyk" không được hiển thị. Có vẻ như nếu nó đã biến mất (?) *

Chuyện gì đã xảy ra? ;-)

Bởi vì có một khoảng trắng giữa "Arkadiusz" và "Wlodarczyk".

ký tự "khoảng trắng" giữa tên và họ là một dấu hiệu cho máy tính rằng có hai biến đang chờ được trích xuất trên luồng 'đầu vào'.

Máy tính nghĩ rằng bạn đang buộc phải gửi đầu vào nhiều hơn một biến. Dấu hiệu "khoảng trống" đó là dấu hiệu để anh ta giải thích nó theo cách đó.

Vì vậy, máy tính gán "Arkadiusz" cho 'name' (2) và vì bạn đặt nhiều chuỗi trên luồng (đầu vào) nên máy tính sẽ cố gắng gán giá trị "Wlodarczyk" cho biến 'age' (!). Người dùng sẽ không có cơ hội đặt bất cứ thứ gì vào 'cin' ở dòng 6 vì lệnh đó đã được thực thi (!). Tại sao? Vì vẫn còn thứ gì đó trên luồng. Và như tôi đã nói trước đó, luồng đang trong luồng nên mọi thứ phải được xóa khỏi nó càng sớm càng tốt. Và khả năng xảy ra khi máy tính nhìn thấy lệnh cin >> age;

Máy tính không biết rằng bạn đã tạo một biến lưu trữ tuổi của ai đó (dòng 4). 'age' chỉ đơn thuần là một nhãn. Đối với máy tính 'tuổi' cũng có thể được gọi là: 'afsfasgfsagasggas' và nó sẽ giống như vậy. Đối với anh ta, đó chỉ là một biến mà anh ta sẽ cố gắng gán "Wlodarczyk" vì bạn đã ra lệnh / hướng dẫn máy tính làm như vậy theo dòng (6).

Làm như vậy là sai, nhưng này chính bạn là người đã làm điều đó! Đó là lỗi của bạn! Chà, có thể là người dùng, nhưng vẫn ...


Tốt thôi tốt thôi. Nhưng làm thế nào để sửa chữa nó?!

Hãy thử làm với ví dụ đó một chút trước khi chúng tôi sửa nó đúng cách để học thêm một vài điều thú vị :-)

Tôi muốn thực hiện một cách tiếp cận mà chúng tôi hiểu mọi thứ. Sửa chữa một cái gì đó mà không có kiến ​​thức làm thế nào chúng tôi đã làm nó không mang lại sự hài lòng, bạn có nghĩ vậy không? :)

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 :-)

Sau khi gọi mã trên, bạn sẽ nhận thấy rằng trạng thái của luồng của bạn (cin) bằng 4 (dòng 7). Có nghĩa là trạng thái bên trong của nó không còn bằng goodbit nữa. Có gì đó rối tung lên. Nó khá rõ ràng, phải không? Bạn đã cố gắng gán giá trị kiểu chuỗi ("Wlodarczyk") cho biến kiểu int 'age'. Các loại không khớp. Đã đến lúc thông báo rằng có điều gì đó không ổn. Và máy tính thực hiện điều đó bằng cách thay đổi trạng thái nội bộ của luồng. Nó giống như: "You f **** up man, làm ơn sửa lỗi cho tôi. Tôi thông báo cho bạn 'vui lòng' ;-)"

Bạn chỉ đơn giản là không thể sử dụng 'cin' (luồng) nữa. Nó bị kẹt. Giống như nếu bạn đặt những khúc gỗ lớn trên dòng nước. Bạn phải sửa chữa nó trước khi bạn có thể sử dụng nó. Không thể lấy dữ liệu (nước) từ dòng suối đó (cin) nữa vì gỗ tròn (trạng thái bên trong) không cho phép bạn làm như vậy.

Ồ vậy nếu có chướng ngại vật (khúc gỗ), chúng ta có thể loại bỏ nó bằng cách sử dụng các công cụ được tạo ra để làm như vậy?

Đúng!

trạng thái bên trong của cin đặt thành 4 giống như máy báo động đang hú và phát ra tiếng ồn.

cin.clear xóa trạng thái trở lại bình thường (goodbit). Giống như thể bạn đã đến và tắt chuông báo thức. Bạn chỉ cần đặt nó đi. Bạn biết điều gì đó đã xảy ra nên bạn nói: "Đừng làm ồn nữa, tôi biết có gì đó không ổn rồi, im lặng (rõ ràng)".

Được rồi, hãy làm như vậy! Hãy sử dụng cin.clear ().

Gọi mã bên dưới bằng cách sử dụng "Arkadiusz Wlodarczyk" làm đầu vào đầu tiê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 :-)

Chúng ta chắc chắn có thể thấy sau khi thực thi đoạn mã trên rằng trạng thái bằng với goodbit.

Tuyệt vời để vấn đề được giải quyết?

Gọi mã bên dưới bằng cách sử dụng "Arkadiusz Wlodarczyk" làm đầu vào đầu tiê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 :-)

Ngay cả khi trạng thái được đặt thành goodbit sau dòng 9, người dùng không được hỏi "tuổi". Chương trình dừng lại.

TẠI SAO?!

Ôi trời ... Bạn vừa tắt báo động, còn khúc gỗ bên trong nước thì sao? * Quay lại tin nhắn nơi chúng ta đã nói về "Wlodarczyk" như thế nào mà nó được cho là đã biến mất.

Bạn cần loại bỏ "Wlodarczyk" mảnh gỗ đó khỏi suối. Tắt báo thức hoàn toàn không giải quyết được vấn đề. Bạn chỉ cần im lặng nó và bạn nghĩ rằng vấn đề đã biến mất? ;)

Vì vậy, đã đến lúc cho một công cụ khác:

cin.ignore có thể được so sánh với một chiếc xe tải đặc biệt với dây thừng đi kèm và loại bỏ các khúc gỗ mắc kẹt bên dòng suối. Nó xóa sự cố mà người dùng chương trình của bạn đã tạo ra.

Vậy chúng ta có thể sử dụng nó ngay cả trước khi báo thức kêu không?

Đúng:

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" sẽ được gỡ bỏ trước khi tạo ra tiếng ồn ở dòng 7.

10000 và '\ n' là gì?

Nó cho biết xóa 10000 ký tự (chỉ trong trường hợp) cho đến khi gặp '\ n' (ENTER). BTW Nó có thể được thực hiện tốt hơn bằng cách sử dụng numeric_limits nhưng nó không phải là chủ đề của câu trả lời này.


Vì vậy, nguyên nhân chính của vấn đề đã biến mất trước khi tiếng ồn được tạo ra ...

Tại sao chúng ta cần 'rõ ràng' sau đó?

Điều gì sẽ xảy ra nếu ai đó đặt câu hỏi 'cho tôi tuổi của bạn' ở dòng 6, ví dụ: "hai mươi tuổi" thay vì viết 20?

Các loại không khớp lại. Máy tính cố gắng gán chuỗi cho int. Và báo thức bắt đầu. Bạn thậm chí không có cơ hội để phản ứng trong tình huống như vậy. cin.ignore sẽ không giúp bạn trong trường hợp như vậy.

Vì vậy, chúng ta phải sử dụng clear trong trường hợp như vậy:

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

Nhưng bạn có nên xóa trạng thái 'chỉ trong trường hợp' không?

Dĩ nhiên là không.

Nếu có gì đó sai (cin >> age;) sẽ thông báo cho bạn về điều đó bằng cách trả về false.

Vì vậy, chúng tôi có thể sử dụng câu lệnh có điều kiện để kiểm tra xem người dùng có đặt sai loại trên luồng

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";

Được rồi để chúng tôi có thể khắc phục sự cố ban đầu của mình, chẳng hạn như:

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;
}

Tất nhiên điều này có thể được cải thiện bằng cách sử dụng vòng lặp while chẳng hạn như làm những gì bạn đã làm.

TẶNG KEM:

Bạn có thể tự hỏi. Còn nếu tôi muốn lấy tên và họ trong cùng một dòng từ người dùng? Thậm chí có thể sử dụng cin nếu cin diễn giải mỗi giá trị được phân tách bằng "dấu cách" là một biến khác nhau?

Chắc chắn, bạn có thể thực hiện theo hai cách:

1)

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

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

2) hoặc bằng cách sử dụng hàm getline.

getline(cin, nameOfStringVariable);

và đó là cách thực hiện:

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

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

Tùy chọn thứ hai có thể phản tác dụng trong trường hợp bạn sử dụng nó sau khi bạn sử dụng 'cin' trước getline.

Hãy cùng kiểm tra nào:

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;

Nếu bạn đặt "20" là tuổi, bạn sẽ không được hỏi tênAndSurname.

Nhưng nếu bạn làm theo cách đó:

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

mọi thứ đều ổn.

GÌ?!

Mỗi khi bạn đưa thứ gì đó vào đầu vào (luồng), bạn để lại ký tự màu trắng ở cuối là ENTER ('\ n') Bạn phải nhập giá trị vào bảng điều khiển bằng cách nào đó. Vì vậy, nó phải xảy ra nếu dữ liệu đến từ người dùng.

b) Đặc điểm của cin là nó bỏ qua khoảng trắng, vì vậy khi bạn đang đọc thông tin từ cin, ký tự dòng mới '\ n' không quan trọng. Nó bị bỏ qua.

a) Hàm getline đưa toàn bộ dòng lên đến ký tự dòng mới ('\ n'), và khi ký tự dòng mới là thứ đầu tiên thì hàm getline nhận được '\ n', và đó là tất cả những gì cần làm. Bạn trích xuất ký tự dòng mới được để lại trên luồng bởi người dùng đặt "20" trên luồng ở dòng 3.

Vì vậy, để khắc phục nó là luôn luôn gọi cin.ignore (); mỗi lần bạn sử dụng cin để nhận bất kỳ giá trị nào nếu bạn định sử dụng getline () bên trong chương trình của mình.

Vì vậy, mã thích hợp sẽ là:

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;

Tôi hy vọng các luồng rõ ràng hơn cho bạn biết.

Hah làm ơn im lặng cho tôi! :-)


1

sử dụng cin.ignore(1000,'\n')để xóa tất cả các ký tự trước đó cin.get()trong bộ đệm và nó sẽ chọn dừng khi gặp '\ n' hoặc 1000 charsđầu tiên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.