【问题标题】:C++: strange behavior with std::istream or sentry wrap aroundC++:std::istream 或哨兵环绕的奇怪行为
【发布时间】:2012-02-29 13:57:30
【问题描述】:

这个小的自定义 getline 函数以 answer 的形式提供给有关处理不同行尾的问题。

该函数运行良好,直到 2 天前对其进行了编辑,使其不会跳过每行的前导空格。然而,在编辑之后,程序现在进入一个无限循环。对代码所做的唯一更改是以下行:

std::istream::sentry se(is);  // When this line is enabled, the program executes
                              // correctly (no infinite loop) but it does skip
                              // leading white spaces

到这里:

std::istream::sentry se(is, true); // With this line enabled, the program goes 
                                   // into infinite loop inside the while loop  
                                   // of the main function.

如果我们指定不跳过空格,有人可以帮我解释为什么程序会无限循环吗?

这是完整的程序...

std::istream& safeGetline(std::istream& is, std::string& t)
{
    t.clear();

    // The characters in the stream are read one-by-one using a std::streambuf.
    // That is faster than reading them one-by-one using the std::istream.
    // Code that uses streambuf this way must be guarded by a sentry object.
    // The sentry object performs various tasks,
    // such as thread synchronization and updating the stream state.

    std::istream::sentry se(is, true);
    std::streambuf* sb = is.rdbuf();

    for(;;) {
        int c = sb->sbumpc();
        switch (c) {
        case '\r':
            c = sb->sgetc();
            if(c == '\n')
                sb->sbumpc();
            return is;
        case '\n':
        case EOF:
            return is;
        default:
            t += (char)c;
        }
    }
}

这是一个测试程序:

int main()
{
    std::string path = "end_of_line_test.txt"

    std::ifstream ifs(path.c_str());
    if(!ifs) {
        std::cout << "Failed to open the file." << std::endl;
        return EXIT_FAILURE;
    }

    int n = 0;
    std::string t;
    while(safeGetline(ifs, t))   //<---- INFINITE LOOP happens here. <----
        std::cout << "\nLine " << ++n << ":" << t << std::endl;

    std::cout << "\nThe file contains " << n << " lines." << std::endl;
    return EXIT_SUCCESS;
}

我也尝试在函数的最开始添加这一行,但没有任何区别......程序仍然在main函数的while循环中无限循环。

is.setf(0, std::ios::skipws);

文件end_of_line_test.txt是一个文本文件,只包含以下两行:

   "1234" // A line with leading white spaces
"5678"    // A line without leading white spaces

【问题讨论】:

  • @templatetypedef 我正在使用最新版本的命令行编译器,用于在 windows xp 32 位上运行的 Visual c++。如果我将哨兵行更改为“std::istream::sentry se(is);”该程序有效,但如果我将其改回“std::istream::sentry se(is, true);”程序崩溃。在您的测试文件中,您是否在任何行中有前导空格?
  • 您遇到了什么具体的崩溃问题?我们可以得到堆栈跟踪吗?
  • 我更改了 while 循环内的行以演示崩溃。我得到一个无休止的打印输出,显示 t 的内容的行号和数字零。我不知道如何进行堆栈跟踪(初学者,抱歉)。
  • @templatetypedef 非常抱歉,我遇到了无限循环。我将编辑我的问题以进行更正。
  • 我已经更新了原始答案stackoverflow.com/questions/6089231/…中的代码现在一切正常。

标签: c++ istream sentry


【解决方案1】:

问题在于safeGetLine 从未为流设置eof() 状态。

当您使用std::istream::sentry se(is); 时, 将尝试读取空格并发现您处于文件末尾。当你要求它不要寻找空格时,这永远不会发生。

我认为您应该将is.setstate(ios_base::eofbit) 添加到函数的EOF 条件中。

【讨论】:

  • 非常感谢您的回答。我在 getline 函数的 case EOF:return is; 之间添加了 is.setstate(ios_base::eofbit)。但是,现在程序在打印一行后停止。我是否需要清除函数开头的流状态位以便它每次都执行?还是那样危险?
  • 您必须将 EOF 案例与 '\n' 案例分开,因为它们(现在)不同。
  • @Bo... 非常感谢,您的回答完美解决了问题!!!!!!我现在能够得到我正在寻找的行为。
  • 优秀的话题。我一直在关注同一个帖子。 @Bo,你能举一个“分开案件”的例子吗?我应该在 case '/n': 之后放什么?我在想“回报是;”不过好笑。它似乎可以正常工作而无需在那里进行任何修改。谢谢
  • @Miek - 我认为它是否有效取决于系统。在 Windows 上,您将使用 \r 分支进行换行,但在 Linux 上,您最终会使用 \n 分支。通过“分离案例”我只是说\nEOF 是分开的,因此需要单独的代码。
猜你喜欢
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-06
  • 2020-06-11
  • 2013-03-05
相关资源
最近更新 更多