簡體   English   中英

C ++:如果條件被認為輸入(浮點數)很差,盡管應該使用cin.clear()和cin.ignore()進行了檢查

[英]C++ : if condition is evaluated with bad input (float) despite supposedly checking for it with cin.clear() and cin.ignore()

以為我了解使用cin.clear()和cin.ignore()處理錯誤的輸入,就像這里解釋的那樣,但是在下面的示例中

#include <iostream>
#include <limits>
using namespace std; //I know that this isn't good practice.

int main () {
    int a, b;

    while (cout << "Input some int: " && !(cin >> a)) {
        cout << "Wrong datatype!\n";
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }

    while (cout << "Input some int: " && !(cin >> b)) {
        cout << "Wrong datatype!\n";
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }

    if (a > 1) cout << "Some event.\n";
    if (b > 1) cout << "Some other event.\n";

    return 0;
}

我想要的行為僅在不需要的輸入是某些字符時出現。 因此,如果我輸入x和y,將再次要求我輸入兩個int並獲得適當的輸出,如果我兩次輸入char和int則相同。

但是:如果輸入2.3,我將得到

輸入一些int:數據類型錯誤!

但由於結果始終輸出“某些事件”,因此沒有機會糾正我的輸入。 第二個提示只是立即接受浮點數。

實際上,正在發生的事情是第一個提示接受了2.32 ,在輸入緩沖區中保留了.3 Wrong datatype! 您看到的是從第二個提示符看到的. ,它不是整數的有效字符。 然后,我假設您輸入一個被第二個提示接受的整數。

這里的問題是,當您在int cin輸入2.3類的內容時就可以了。 它讀取2 ,看到. 因此它將停止讀取並將2儲存在變量中,並將.3保留在緩沖區中以備下次調用。 因此,您傳遞了第一個循環,進入了第二個循環,然后由於嘗試讀取中而失敗. 進入b 然后清除.3 ,然后可以輸入其他輸入。 如果輸入另一個2.3 ,則將發生相同的情況, b將變為2 ,程序將繼續。

讀入輸入的“防彈”方法是將其讀為std::string ,然后進行解析以確保完整的輸入是正確的。 看起來像

std::string line;
while (cout << "Input some int: " && std::getline(cin, line)) {
    std::stringstream ss(line);
    ss >> a;
    if (ss.eof()) // we did consume all the input
        break;
    else
        cout << "Wrong datatype!\n";
}

while (cout << "Input some int: " && std::getline(cin, line)) {
    std::stringstream ss(line);
    ss >> b;
    if (ss.eof()) // we did consume all the input
        break;
    else
        cout << "Wrong datatype!\n";
}

這種基本方法是脆弱的,並且容易出錯。

您的明顯意圖是接受一行輸入並進行處理。 如果是這樣,那么執行此操作的正確函數是std::getline() 這就是它的目的。 那正是它的作用。 >>運算符不會這樣做。 那不是它的目的。 當然,通過使用ignore()clear()等各種輔助方法,仍然可以實現該目標,但是,正如您所發現的那樣,正確使用這些函數並不直觀。 當然,您可以花費大量時間來查看他們的文檔,以了解他們的每種語義行為,但是當您只需使用std::getline()然后轉到其他內容時,何必麻煩一下。 這樣做更容易。

當然,一旦收到一行輸入,您想將其解析為整數。 現在是使用>>進行解析的正確時間:

std::string line;

if (std::getline(line, std::cin))
{
    std::istringstream i{line};

    int n;

    if (i >> n)
    {
           // Input parsed
    }
}

這不是更簡單,更直接,更簡單嗎? 當然,在此處輸入“ 2.3”將導致>>運算符解析“ 2”,並成功,而未解析“ .3”。 如果您想檢測這種情況,只需使用get()來查看std::istringstream剩下什么。 如果願意,也許接受任何結尾的空格。

當您輸入“ 2.3”時,cin將停在“。”,並將“ 2”解釋為所需的輸入。

然后,您將cin清除為'。'。 遇到,丟棄3。

如果您隨后輸入一個新的整數,它將接受它。

此處有許多答案建議使用std :: getline和字符串解析,或者使用字符串函數或stringstreams。 這是非常低效的,而不是應該使用流的方式。

而是在數據仍在輸入流中時對其進行解析:

#include <iostream>
#include <cctype>
#include <limits>

struct read_int {
    int& a;

    read_int(int& aa) : a{ aa } { }
    friend std::istream& operator >>(std::istream& is, read_int& ri) {
        char delim;
        while(!(is >> ri.a) || (delim = is.get(), delim != '\n' && !std::isspace(delim))) {
            std::cerr << "Bad!\n";
            is.clear();
            is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        return is;
    }
};


int main() {
    int a, b;
    std::cin >> read_int(a) >> read_int(b);
    std::cout << a << ' ' << b;
    return 0;
}

該函數將接受“ 4 5”或“ 4 \\ n6”之類的輸入,但請求諸如“ 4.2”之類的數據的新輸入,丟棄之前讀取的所有內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM