【问题标题】:How to eliminate this extra element?如何消除这个额外的元素?
【发布时间】:2014-09-16 09:46:45
【问题描述】:

我正在编写一个用于读取制表符分隔文本文件的第 n 列的 c++ 函数,这是我所做的:

typedef unsigned int  uint;


inline void fileExists (const std::string& name) {
    if ( access( name.c_str(), F_OK ) == -1 ) {
        throw std::string("File does not exist!");
    }
}

size_t bimNCols(std::string fn) {
    try {
        fileExists(fn);
        std::ifstream in_file(fn);
        std::string tmpline;
        std::getline(in_file, tmpline);
        std::vector<std::string> strs;
        strs = boost::split(strs, tmpline, boost::is_any_of("\t"), boost::token_compress_on);
        return strs.size();
    } catch (const std::string& e) {
        std::cerr << "\n" << e << "\n";
        exit(EXIT_FAILURE);
    }
}

typedef std::vector<std::string> vecStr;

vecStr bimReadCol(std::string fn, uint ncol_select) {
    try {
        size_t ncols = bimNCols(fn);
        if(ncol_select < 1 or ncol_select > ncols) {
            throw std::string("Your column selection is out of range!");
        }

        std::ifstream in_file(fn);
        std::string tmpword;
        vecStr colsel; // holds the column of strings
        while (in_file) {
            for(int i=1; i<ncol_select; i++) {
                in_file >> tmpword;
            }
            in_file >> tmpword;
            colsel.push_back(tmpword);
            in_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        return colsel;

    } catch (const std::string& e) {
        std::cerr << "\n" << e << "\n";
        exit(EXIT_FAILURE);
    }
}

问题在于,在bimReadCol 函数中,在最后一行之后

in_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

in_file.good() 的计算结果仍为 true。所以,假设我有一个像这样的文本文件test.txt

a 1 b 2
a 1 b 2
a 1 b 2

bimReadCol("test.txt", 3) 将返回一个向量(b, b, b, b),并带有一个额外的元素。 知道如何解决这个问题吗?

【问题讨论】:

  • 注意:请使用返回值和更少的异常 - 如果您使用异常,请从 std::exception 派生
  • @DieterLücking 您能否提供有关返回值与异常的参考?我不知道如何将返回值用于与异常相同的目的。
  • 不,但在我看来fileExists当然不应该抛出异常。
  • 哦,那个。但为什么不呢?
  • @DieterLücking 我刚刚发现创建一个带有自定义错误消息的异常类太麻烦了,所以我想,为什么不直接抛出一个错误消息呢?你能解释一下为什么这很糟糕吗?

标签: c++ file io


【解决方案1】:

面向行的输入的通常解决方案是读取 line by 行,然后解析每一行:

std::string line;
while ( std::getline( in_file, line ) ) {
    std::istringstream parser( line );
    for ( int i = 1; parser >> tmpword && i <= ncol_select; ++ i ) {
    }
    if ( parser ) {
        colsel.push_back( tmpword );
    }
    //  No need for any ignore.
}

重要的是你必须绝对测试之后 在使用 价值。读取值之前的测试没有任何意义 (如您所见)。

【讨论】:

  • if(parser) 测试是什么?
  • 这是std::istringstream 的演员表,它是here
  • @luk32 不是强制转换,而是转换
  • 很公平。但它不是叫做类型转换运算符吗?例如:12。区别只在于显式还是隐式?
  • @luk32 在标准中,至少,强制转换是导致转换的几个不同运算符之一。转换可能是强制转换(显式转换)或其他(隐式转换)的结果。
【解决方案2】:

好的,我明白了。文本文件的最后一行不包含换行符,这就是 in_file 计算的原因 最后一行的true

我想我应该计算文件的行数,然后将while(in_file) 替换为 for 循环。

如果有人有更好的想法,请发表,我会接受。

更新

修复结果相当简单,只需检查tmpword 是否为空:

vecStr bimReadCol(std::string fn, uint ncol_select) {
    try {
        size_t ncols = bimNCols(fn);
        if(ncol_select < 1 or ncol_select > ncols) {
            throw std::string("Your column selection is out of range!");
        }

        std::ifstream in_file(fn);
        vecStr colsel; // holds the column of strings
        std::string tmpword;
        while (in_file) {
            tmpword = "";
            for(int i=1; i<=ncol_select; i++) {
                in_file >> tmpword;
            }
            if(tmpword != "") {
                colsel.push_back(tmpword);
            }
            in_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        return colsel;

    } catch (const std::string& e) {
        std::cerr << "\n" << e << "\n";
        exit(EXIT_FAILURE);
    }
}

正如@James Kanze 所指出的,即使最后一行包含换行符,in_file 仍会评估为true,但由于我们在文件末尾,下一次读入 tmpword 将是空的,所以只要我们检查一下就可以了。

【讨论】:

  • 更有可能的是,文本文件的最后一行确实包含一个新行。您遇到的问题是忽略下一个'\n' 不会设置任何错误状态,即使您到达文件末尾。仅当您尝试读取超出文件末尾的内容时才会设置错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-06
  • 2013-04-19
  • 2012-12-03
  • 1970-01-01
  • 2016-09-14
  • 1970-01-01
  • 2013-01-28
相关资源
最近更新 更多