【问题标题】:How to read unsigned int variables from file correctly, using ifstream?如何使用 ifstream 从文件中正确读取 unsigned int 变量?
【发布时间】:2012-01-21 12:22:31
【问题描述】:

我的代码从文本文件Input_File_Name 中读取无符号整数变量。

unsigned int Column_Count; //Cols
unsigned int Row_Count;//Rows

try {
    ifstream input_stream;
    input_stream.open(Input_File_Name,ios_base::in);
    if (input_stream) {
        //if file is opened
        input_stream.exceptions(ios::badbit | ios::failbit);
        input_stream>>Row_Count;
        input_stream>>Column_Count;


    } else {
        throw std::ios::failure("Can't open input file");
        //cout << "Error: Can't open input file" << endl;
    }

} catch (const ios::failure& error) {
    cout << "Oh No!!" << error.what() << endl;          
} catch (const exception& error) {
    cout << error.what() <<"Oh No!!" << endl;
} catch (...) {
    cout << "Unknown exception" << endl;
}

效果很好。 但是当我用错误的数据填充文本文件时

33abcd4  567fg8

它是这样工作的:

input_stream>>Row_Count; //Row_Count = 33;
input_stream>>Column_Count; // throws an ios::failure exception

为什么input_stream&gt;&gt;Row_Count;这一行不抛出异常? 据我了解, input_stream 将任何非数字符号视为分隔符,并在下一步尝试读取“abcd”。是这样吗? 如何在读取“33abcd4”时将空格符号设置为分隔符以从这行代码input_stream&gt;&gt;Row_Count; 中抛出ios::failure 异常?

【问题讨论】:

  • operator&gt;&gt; 就是这样工作的;它将提取“33”然后停止,“abcd”留在流中以供下次调用operator&gt;&gt;。您可以改为将33abcd4 读入字符串,然后检查其中是否存在非数字字符。此外,如果您有支持 C++11 的最新编译器,请检查标准库是否提供 std::stoull

标签: c++ file ifstream


【解决方案1】:

如果流可以读取任何整数值,则整数值的正常提取成功。也就是说,如果至少有一个数字可选地后跟任何数字,则整数的读取成功。正常的提取操作尝试读取更多内容,特别是它们不尝试查找下一个空格。

从它的声音来看,您希望确保您的号码后面有一个空格,如果没有则失败。我可以想到两种不同的方法来做到这一点:

  1. 创建一个简单的操纵器来检查流是否在空白字符上。但是,这意味着您将使用 in &gt;&gt; value &gt;&gt; is_space 之类的方式读取您的值。
  2. 创建一个自定义std::num_get&lt;char&gt; facet,将其安装到std::locale,并将imbue() 这个std::locale 安装到您的流中。它涉及更多一点,但不需要对整数的读取方式进行任何更改。

创建这样的操纵器相当简单:

std::istream& is_space(std::istream& in)
{
    if (!std::isspace(in.peek()))
    {
        in.setstate(std::ios_base::failbit);
    }
    return in;
}

现在,改变数字的读取方式更有趣,我怀疑我刚刚命名了一些大多数人都不知道的标准库类。因此,让我们也快速输入一个示例。我将更改std::num_get&lt;char&gt; 方面仅处理unsigned int:要为其他整数类型执行此操作,必须重写更多函数。所以,这里是std::num_get&lt;char&gt; facet 的替代品:

class num_get:
    public std::num_get<char>
{
    iter_type do_get(iter_type it, iter_type end,
                     std::ios_base& ios, std::ios_base::iostate& err,
                     unsigned int& value) const
    {
        it = std::num_get<char>::do_get(it, end, ios, err, value);
        if (it != end && !isspace(static_cast<unsigned char>(*it)))
        {
            err |= std::ios_base::failbit;
        }
        return it;
    }
};

所有这些都是从std::num_get&lt;char&gt; 派生一个类并覆盖它的一个虚函数。这个函数的实现相当简单:首先通过委托基类读取值(我刚刚意识到虚函数确实想要保护而不是像过去那样私有,但这是一个完全不同的讨论) .与这是否成功无关(如果不是,它将在 err 中设置错误状态),覆盖检查是否有另一个字符可用,如果有,则检查它是否是空格,如果没有则设置 @987654332错误结果中的@err

剩下的就是设置流以在std::locale 中使用这个特定方面并将新的std::locale 挂钩到流中:

std::locale loc(std::locale(), new num_get);
in.imbue(loc);

std::locales 及其构面是内部引用计数的,即您不应该跟踪指向构面的指针,也不需要保留 std::locale。如果imbue() 创建的std::locale 看起来很麻烦,或者您想在任何地方使用这个修改后的逻辑,您可以设置用于初始化任何新创建的流的全局std::locale 以使用自定义std::num_get&lt;char&gt;方面。

【讨论】:

    【解决方案2】:

    你可以这样做:

    #include <iostream>
    #include <locale>
    
    class my_num_get : public std::num_get<char> {
    protected:
        iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned int& v) const
        {
            in = std::num_get<char>::do_get(in, end, str, err, v);
            if(in != end && !std::isspace(*in, str.getloc()))
                err |= std::ios_base::failbit;
            return in;
        }
    };
    
    int main() {
        using namespace std;
        cin.imbue(std::locale(cin.getloc(), new my_num_get));
        cin.exceptions(ios_base::badbit | ios_base::failbit);
        try {
            unsigned int x;
            cin >> x;
        } catch(const std::exception &e) {
            cerr << e.what() << "\n";
        }
    }
    

    如果您希望它也适用于其他类型,请以相同的方式实现以下内容:

    iter_type do_get(iter_type, iter_type, ios_base&, ios_base::iostate&, T& v) const
    

    其中 T 是boollonglong longunsigned shortunsigned longunsigned long longfloatdoublelong double 和 @987654332 之一。 p>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      • 2012-03-23
      • 2010-12-09
      • 2016-03-19
      • 1970-01-01
      相关资源
      最近更新 更多