2015-03-24 94 views
1

我的C++教授堅持在檢查輸入失敗時,必須爲每個單獨的輸入使用單獨的while()循環。他表示,檢查多於一個輸入以下方法聚集在一個cin說法是行不通的:C++輸入失敗:單個語句中有多個輸入

while (!good){ 
    cout << "Enter the length and the width of the rectangle: "; 
    cin >> length >> width; 
    if(!cin){ 
     cout << "Bad input, try again" << endl; 
     cin.clear(); 
     cin.ignore(200, '\n');} 
    else { 
     good = true;}} 

他提出的方法:以上

bool good = false; 
while (!good){ 
    cout << "Enter the length rectangle: "; 
    cin >> length; 
    if(!cin){ 
     cout << "Bad input, try again" << endl; 
     cin.clear(); 
     cin.ignore(200, '\n');} 
    else { 
     good = true;}} 
while (good){ 
    cout << "Enter the width rectangle: "; 
    cin >> width; 
    if(!cin){ 
     cout << "Bad input, try again" << endl; 
     cin.clear(); 
     cin.ignore(200, '\n');} 
    else { 
     good = false;}} 

我的方法似乎工作就好了。如果輸入了任何一個輸入的非數字字符,則循環將提示輸入新的信息並清除故障狀態。

它是否被認爲是不正確的形式來檢查輸入失敗?我希望更清楚地瞭解爲什麼我的方法是錯誤的。如果我的方法有問題,是否有辦法檢查輸入失敗,而不用爲每個用戶輸入使用單獨的循環?我主要問,因爲我正在編寫一個程序,涉及從文件中獲取許多輸入,並且單獨檢查每個變量似乎過於乏味。我在想,一定有更好的辦法。

+0

你的教授需要了解這種新語法(只有40歲)被稱爲一個做一段時間。 – 2015-03-24 23:57:32

+0

爲了更接近希臘化名:更多關注,兩種代碼都不檢查「eof」標誌。 – 2015-03-24 23:59:06

+0

您可能會發現[C++ FAQ:如何獲得std :: cin來跳過無效輸入字符?](https://isocpp.org/wiki/faq/input-output#istream-and-ignore)有用(請參閱第二個代碼示例。) – 2015-03-25 00:02:24

回答

3

兩者都是「正確的」,因爲它們從失敗的輸入中恢復並給用戶另一個機會。

兩者僅用於交互式輸入。你提到從文件中讀取 - 真的沒有恢復可能。忽略損壞的字段將簡單地導致下一個字段被消耗,並且的解釋與它在文件中的位置指示的位置不同。從文件中讀取時的最佳操作過程是儘可能輸出解釋(文件中錯誤發生的位置,例如行和列號,預期的數據類型以及遇到的數據無效) 。

在交互式使用中,清除緩衝區和再次提示很有用..您的版本具有的缺點是,在正確輸入長度後,如果用戶胖了手指的寬度,他們不能只是重新輸入錯誤的數據,但也必須再次輸入長度。

然而,第二次寫入循環是難以置信的無意義的。相反,你應該寫一個輔助函數,是這樣的:

template<typename T> 
T prompt_for_value(const char* const prompt) 
{ 
    T result; 
    while (true) { 
     std::cout << prompt << std::flush; 
     if (std::cin >> result) return result; 
     std::cout << "Bad input, try again" << std::endl; 
    } 
} 

double width = prompt_for_value<double>("Enter the width in meters: "); 
double height = prompt_for_value<double>("Enter the height: "); 

注意,代碼是整體短,避免了笨重的使用good變量,通過原代碼中途倒它的意義的。此外,呼叫站點現在非常乾淨,完全集中於重要信息 - 輸入的提示和數據類型。

感謝C++ 11的λ的支持,它現在也很容易參數驗證添加到輔助函數:

T prompt_for_value(const char* const prompt, std::function<bool(T)> validation = {}) 
{ 
    T result; 
    while (true) { 
     std::cout << prompt << std::flush; 
     if (std::cin >> result) { 
      if (validation && !validation(result)) { 
       std::cout << "Input out of range, try again" << std::endl; 
       continue; 
      } 
      return result; 
     } 
     std::cout << "Bad input, try again" << std::endl; 
    } 
} 

double width = prompt_for_value<double>("Enter the width in meters: ", 
             [](int w) { return w >= 0; }); 
double height = prompt_for_value<double>("Enter the height: ", 
        [&width](int h) { return (h >= 0) && (h <= 10000.0/width); })); 
+0

如果從文件讀取,這將導致無限循環。 – 2015-03-25 00:14:02

+0

@remyabel:那麼原創。我清楚地表示,這種重試方法僅適用於交互式輸入。我對'cin'和'cout'進行了硬編碼,以減少錯誤地將它用於文件輸入的可能性。你有沒有讀到答案? – 2015-03-25 00:14:57

0

您已經表明什麼是你的教授提出的方法是非常糟糕的形式給予適當的違反代碼中的don't-repeat-yourself (DRY) principle。如果你想多次執行基本相同的事情,你應該使用循環或函數來避免編寫重複的邏輯。

您的方法涉及全或全無檢查,因此可能需要用戶重複自己。這種可用性應該根據具體情況來決定。

這兩種方法都無法在輸入行末尾檢測到無關字符。這樣的輸入可能會被合理地解釋爲錯誤 - 無論它是否恰好適用於隨後的輸入請求。

但是,正如您已經證明的那樣,「全有或全無」方法實際上基本上可行。鑑於此,這裏是基於iostream library能夠自動執行的基於異常的自動檢查的替代全有或全無實現。

cin.exceptions(~std::ios::goodbit); 

    int length=0, width=0; 

    for (bool ok=false; !ok;) 
     { 
     try 
     { 

     cout << "Enter the length and the width of the rectangle: "; 
     cin >> std::skipws >> length >> width; 

     std::string s; 
     std::getline(cin, s); 
     s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); 
     if (!s.empty()) throw std::runtime_error("too much input"); 

     ok = true; 
     } 
     catch(std::ios::failure const & e) 
     { 
     cout<<"\n"<<"bad input ["<<e.what()<<"], try again...\n"; 
     cin.clear(); 
     cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
     } 
     catch(std::runtime_error const & e) 
     { 
     cout<<"\n"<<e.what()<<", try again...\n"; 
     } 
     } 

    cout<<"\n"<<"length="<<length<<", width="<<width<<"\n"; 

輸出:

Enter the length and the width of the rectangle: x 

bad input [basic_ios::clear], try again... 
Enter the length and the width of the rectangle: 1 x 

bad input [basic_ios::clear], try again... 
Enter the length and the width of the rectangle: 1 2 x 

too much input, try again... 
Enter the length and the width of the rectangle: 1 2 3 

too much input, try again... 
Enter the length and the width of the rectangle: 4 5 

length=4, width=5 
+0

檢查尾隨字符是一個不錯的主意,但異常處理增加了複雜性,沒有任何好處。應該使用異常來實現來自調用堆棧中幾層以外的集中式錯誤處理,而不是本地重試。 – 2015-03-25 02:48:32

+0

使用iostreams庫很複雜,至少有可能打開異常使事情變得更簡單 - 特別是如果你想執行多個I/O操作。使用異常讓我更有信心,如果出現問題,我會知道它。我認爲在這裏使用異常而不是手動檢查是沒有什麼壞處的 - 當然執行速度並不是問題。我的實現有更多的代碼行,但主要是支持更多的錯誤條件檢查。 – nobar 2015-03-25 06:46:50

+0

我喜歡指出iostreams的異常模式,尤其是因爲對於基於文件的I/O,通過繞過手動錯誤檢查,可以使得全部或全無的方法變得更清晰/更簡單。我猜這種可比較複雜性的選擇是在完成整個I/O序列之前忽略所有的錯誤,然後*只詢問庫是否真正起作用。也許這是一個合理的方法,但對我來說,它似乎不雅和容易出錯。 – nobar 2015-03-25 13:17:01