【问题标题】:Why is std::ifstream closing by itself?为什么 std::ifstream 会自行关闭?
【发布时间】:2021-10-31 15:03:54
【问题描述】:

我正在以这种方式读取 ascii 文件:name1|name2|name3|name4|name5|name6|name7||||||||||name8|||name9 它包含一堆由“|”分隔的名称char,一些插槽是空的。我正在使用以下代码来读取名称列表:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>

int main() {
    std::ifstream File;
    File.open("File.txt");
    if (!File.is_open())
        return -1;

    std::vector<std::string> Names;
    char Buffer[0xff];
    while (!File.getline(Buffer,16,'|').eof()) {
        if (strlen(Buffer) == 0)
            break;
        Names.push_back(Buffer);

        std::cout << strerror(File.rdstate()) << std::endl;
    }

    return 0;
}

它正在正常工作,它每次都会读取一个名称,但由于某种原因,如果它在File.getline() 的第二个参数上达到字符计数并且没有找到 delimiting 字符,它会自行关闭。这是我在具有大名称的文件上运行此代码后得到的控制台输出:

File: File.txt
Ana|Bjarne Stroustrup|Alex

Console:
No error
No such file or directory

它成功读取列表中的第一个名称,但是当它尝试读取第二个名称时,它没有命中 delimiting 字符,并且由于某种原因导致文件关闭本身。我希望有人能向我解释为什么会发生这种情况。

【问题讨论】:

  • 您应该检查getlinestrlen 函数调用之间的流状态。您可能想阅读以下内容:Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?
  • 寻求调试帮助的问题应提供minimal reproducible example 准确的输入、预期输出和实际输出。
  • The documentation 表示,如果getline 读取count-1 字符并且没​​有遇到分隔符,则设置failbit。一旦流处于此失败状态,所有后续操作都会失败,直到该位被清除。
  • File.clear()。但问题是长名称的其余部分仍然在流中,以及它后面的分隔符,所以现在你需要弄清楚如何处理它。你为什么不使用the version of getline,它需要std::string?它没有任何长度限制。特别是因为您最终将名称存储为std::string
  • @Azazel:是的,新版本的代码更好。但是,我会将while (!File.getline(Buffer,16,'|').eof()) { 更改为while ( File.getline(Buffer,16,'|') ) {。这有两个优点:它更简单,并且它还可以在错误条件下停止循环,而不仅仅是文件结束条件。

标签: c++ ifstream


【解决方案1】:

表达式strerror(File.rdstate()) 没有意义。表达式File.rdstate() 返回一个整数,表示流的状态标志位。这不是错误代码。但是,函数strerror 需要一个错误代码。

因此,一般来说,调用strerror(errno)perror(nullptr) 可能更有意义。但在这种情况下,两者都只是返回或打印"No Error"

在你的问题中,你写道:

它成功读取了列表中的第一个名称,但是当它尝试读取第二个名称时,它没有命中分隔字符,并且由于某种原因导致文件自行关闭。我希望有人能向我解释为什么会发生这种情况。

文件没有关闭。错误信息

No such file or directory

您收到的信息具有误导性。这是您错误使用函数strerror的结果,如上所述。

如果getline 无法读取分隔符,则会导致failbit 被设置。由于流处于失败状态,下一次循环迭代中getline 的下一个函数调用会立即失败,而不会尝试提取任何字符。这会导致if 语句

if (strlen(Buffer) == 0)

为真,导致循环被中止。

【讨论】:

  • 正是,感谢您的回答。我认为这些内部标志只是用来指向程序员对象的状态,而不是它们可以直接影响对象的工作方式。正如@IgorTandetnik 在 cmets 上所指出的那样,为了修复我的 std::ifstream 对象,我必须在其上使用 std::ios:clear() ,因此即使它没有遇到分隔符,它也可以工作。
【解决方案2】:

您可以使用 below 显示的程序将名称从 File.txt 添加到 std::vector

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
int main()
{   
    std::ifstream inputFile("File.txt");
    
    std::string wordName;
    
    std::vector<std::string> Names;
    if(inputFile)
    {
        while(getline(inputFile, wordName, '|'))
        {
            if((wordName.size()!=0))
            {
                Names.push_back(wordName);
            }
        }
    }
    else 
    {
        std::cout<<"File could not be opened"<<std::endl;
    }
    
    inputFile.close();
    
    //lets check/confirm if our vector contains all the names correct names 
    for(const std::string &name: Names)
    {
        std::cout<<name<<std::endl;
    }
    return 0;
}

以上程序的输出可见here

【讨论】:

  • 感谢您的回答。这是一个很好的解决方案,但我仍然需要使用 std::istream::getline() 函数而不是 std::getline() 因为我需要流大小为 16,如示例代码所示。
  • @Azazel:为什么需要getline 的缓冲区大小函数参数为16?为什么不将其设置为缓冲区的实际大小,即255 (0xff)?
  • @Azazel 不客气。
  • 我要读取的文件很大,我省略了一些信息,以免使这个问题过于复杂,所以我使用缓冲区大小为 16,因为我没有不想最终从另一个信息源中找到另一个分隔符,如果我没有找到任何一个,我只需将其余信息附加到最后一个字符串上,例如它将显示为“|Bjarne Stroustrup;70| "如:“Bjarne Stroustr”,然后是“up;75|”。我会解释';' char as '\0' 然后追加,所以最后的字符串是:“Bjarne Stroustr”+“up”,“Bjarne Stroustrup”。
猜你喜欢
  • 2019-08-25
  • 2013-05-09
  • 1970-01-01
  • 2020-05-31
  • 2017-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多