繁体   English   中英

如何使用ifstream从文件正确读取未签名的int变量?

[英]How to read unsigned int variables from file correctly, using ifstream?

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

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>>Row_Count; 抛出异常? 据我了解,input_stream会将任何非数字符号视为定界符,并在下一步中尝试读取“ abcd”。 是这样吗? 如何将空格符号设置为定界符,以从此行代码中抛出ios::failure异常input_stream>>Row_Count; 在阅读“ 33abcd4”时?

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

从声音的角度来看,您想确保数字后面有空格,如果没有空格,则会失败。 我可以想到两种不同的方法来做到这一点:

  1. 创建一个简单的操纵器,检查流是否在空白字符上。 但是,这意味着您将使用in >> value >> is_space类的内容读取值。
  2. 创建一个自定义std::num_get<char>构面,将其安装到std::locale ,并将此std::locale嵌入imbue()到您的流中。 它涉及更多点,但不需要更改读取整数的方式。

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

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

现在,更改数字的读取方式更加有趣,我怀疑我刚刚为大多数人都不太了解的一些标准库类命名了。 因此,我们也快速输入一个示例。 我将仅更改std::num_get<char>方面来处理unsigned int :要对其他整数类型执行此操作,必须重写更多函数。 因此,这是std::num_get<char>构面的替代品:

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<char>派生一个类并重写其虚拟函数之一。 该函数的实现非常简单:首先通过委派基类来读取值(我刚刚意识到,虚函数确实希望像过去那样保护而不是私有,但这是一个完全不同的讨论) 。 不管是否成功(如果失败,它将在err中设置错误状态),重写将检查是否有另一个字符可用,如果是,则检查是否为空格,如果没有则设置std::ios_base::failbit在错误结果err

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

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

std::locale及其构面在内部被计数,即,您不应该跟踪指向构面的指针,也不需要在任何一个周围保持std::locale 如果对创建的std::locale进行imbue()似乎很麻烦,或者您想在各处使用此修改后的逻辑,则可以将全局std::locale为用于初始化任何新创建的流以使用自定义std::num_get<char>构面。

您可以这样操作:

#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 doublevoid*

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM