【问题标题】:Don't accept decimals as an input [duplicate]不接受小数作为输入[重复]
【发布时间】:2014-05-20 17:58:25
【问题描述】:

我必须编写一个程序,要求用户输入一个数字,如果他们输入零,它将打印出他们输入的零,如果他们输入负数或正数,它将打印出他们输入的任何一个一个负数或正数。我有它,所以它不接受字母和逗号等。但我不知道如何让这个不接受小数?任何线索我怎么能做到这一点?除了 cplusplus.com 之外,任何具有良好 c++ 参考的好网站

#include <iostream>
#include <string>
#include <limits>
#include <cmath>
#include <iomanip>
#include <cstdlib>

using namespace std;

    int getInt()
    {
    int choice=0;
    while (!(cin >> choice))
        {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(),'\n');
        cout << "Please input a valid integer: " << '\n';
        }
    return (choice);
    }

int print_zero()
{
 cout << "The number you entered is a zero. " << '\n';
 return 0;
}

int print_negative()
{
 cout << "You entered a negative number. " << '\n';
 return 0;
}

int print_positive()
{
    cout << "You entered a positive number. " << '\n';
    return 0;
}

int main ()
    {

    cout << "your number please:-" << '\n';
    int choice = getInt();

    if (choice == 0)
    {
        print_zero();
    }

    if (choice < 0)
    {
        print_negative();
    }

    if (choice > 0)
    {
        print_positive();
    }

cout << endl << "All done! Nice!!" << endl;

return 0;
}

【问题讨论】:

  • 术语说明:在这种情况下,您可能应该使用“浮点”而不是“小数”。 “十进制”在许多库和应用程序中具有特殊含义。
  • 您的代码已经拒绝小数点,因为您提取的是整数,而不是浮点变量。
  • @0x499602D2 它将读取该点之前的位并将其转换为 int,但实际上不会出错。
  • '除了 cplusplus.com 之外,任何具有良好 c++ 参考的好网站' 当然:cppreference.com
  • 至于你的问题:cin &gt;&gt; choice 不是已经保证只能给出有效的整数吗?还是您想阻止用户输入 s.th。像7.8 并且它被接受为有效的int(7)?那么你可以选择 Rook 的答案。

标签: c++ error-checking


【解决方案1】:

除了上一个答案,您还可以选择创建自己的std::num_get&lt;char&gt; facet,将您的自定义输入解析无缝集成到 IOStreams 接口中。

如果您在读入整数时碰巧输入了浮点文字,则流仍将解析尽可能多的字符,只要这些字符可用于您要提取的数据类型。当流找到流的结尾、空白字符或不符合类型格式要求的字符时,它才会停止读取。在我们的例子中,流将找到字符.,然后停止读取。

结果是即使部分输入已被消耗,读取也被认为是成功的。然而,下一次读取将不成功,因为下一个字符是 .,它在整数中是不可用的。

这是我们将用于自定义方面的信息。在读取输入后,我们只需检查下一个字符是否为小数点。如果它,你有几个选项来报告错误:

你可以...

  • 输出错误信息

    输出错误信息对控制台用户来说是最方便的,但不符合 IOStreams 的设计。当检测到错误输入时,流不会将错误消息输出到控制台,因此您的方面也不应该。

  • 抛出异常

    您可以抛出异常,但请注意它们不会传播到流之外。这是因为默认情况下流被编程为不抛出异常。相反,只要检测到异常,他们就会在流中设置std::ios_base::badbit。您必须在执行输入之前或之后在流上设置 exceptions() 掩码以捕获异常。另一个需要注意的是,只有std::ios_base::failure 是从流中抛出的,所以你只能捕捉到它。

  • 设置流状态

    设置流状态对您方面的用户最有意义,并且符合 IOStreams 的设计。这样,您就不必彻底改变使用流的方式。只需在流状态下检查输入是否成功,就像您自然地使用普通 facet 一样。

设置流状态是我们将在以下代码中使用的方法:

#include <locale>

class num_get : public std::num_get<char>
{
public:
    // Override do_get which is a virtual function in the std::num_get<char>
    // base class. It is called by the public member function get() in the
    // implementation of std::basic_istream<charT>::operator>>(int&)

    // You might want to put this into a helper function and call it in
    // both the signed and unsigned overloads

    iter_type do_get( iter_type it, iter_type end, std::ios_base& str,
                      std::ios_base::iostate& err, long& v ) const
    {
        // Store a locale object for later use.
        std::locale loc(str.getloc());

        // delegate the extraction to the default base class function
        it = std::num_get<char>::do_get(it, end, str, err, v);
        
        // If the extraction succeeded, tell the user if positive or negative,
        // or zero
        if (!(err & std::ios_base::failbit))
        {
            if (v == 0)
                std::cout << "The number you entered is a zero.\n";
            std::cout << "You entered a " <<
                ((v >= 0) ? "positive" : "negative") << " number.\n";
                    
            // Check whether the end has not been met (because of further
            // input that can't be used in a long). And if the first character
            // of that input is a decimal point (or '.' in en_US) then get
            // rid of that input for convenience, and set failbit

            if (it != end && *it == std::use_facet<std::numpunct<char>>(loc).decimal_point())
            {
                // We get rid of that input by calling the base class function
                // again which does all the necessary parsing.

                // Note that if you do not want to get rid of the invalid
                // floating point input, then simply remove these two lines.
                it = std::num_get<char>::do_get(++it, end, str, err, v);
                // Clear out v
                v = 0;
                
                // set failbit
                err |= std::ios_base::failbit;
            }
        }    
        return it;
    }
};

要在流中设置此构面,请将其安装到语言环境中并将该语言环境“灌输”到流中。像这样:

// Create a new locale with std::num_get<char> facet replaced by our custom facet
std::locale new_locale(std::cin.getloc(), new num_get);
// Imbue this new locale into std::cin
std::cin.imbue(new_locale);

完成后无需删除构面。这由保存它的语言环境的析构函数处理。

如果您想获得不同的行为,则应在实际使用流之前完成本地化设置。

Live Example

【讨论】:

    【解决方案2】:

    一个相当简单的事情是使用类似的东西

    std::string line;
    std::getline(std::cin, line);
    
    size_t pos;
    int x = 0;
    
    try
    {
      x = std::stoi(line, &pos);
    
      if (pos < line.length())
      {
        std::cout << "Warning, non-digit character " << line[pos] << " detected!\n"
        return;
      }
    }
    catch (std::exception&)
    {
      std::cout << "That didn't look like an integer to me.\n";
      return;
    }
    

    getline 从输入中获取所有文本,而不是仅仅停留在无法转换为您请求的格式的第一个字符(例如int)。它也为您摆脱了任何不方便的尾随 \n

    std::stoi 执行从std::stringint 的转换。阅读docs,如果你不小心它会抛出异常!它返回pos 中第一个未转换字符的位置。如果pos 小于line 的长度,则表示其中存在不属于int 的无效字符。

    【讨论】:

    • 这不考虑负数,只允许列表中的“-”字符将允许格式错误的数字,如“5-2”。
    • getlinefind_first_not_ofcin 中删除字符串末尾的'\n' 字符?
    • getline 确实应该去掉\n
    • @YoungJohn 现在可能已修复。让std::stoi 进行过滤,我可能一开始就应该这样做。
    • @TechyGirl98 这里没有足够的空间来详细介绍,但这称为“异常处理”。它们不是函数,而是控制结构,就像if 不是函数一样。异常是 C++ 处理错误的一种方式。这是一个复杂的主题,但非常值得一读!谷歌会提供帮助。
    猜你喜欢
    • 2020-04-12
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多