6

在下面的 C++ 代码中,我意识到gcount()返回的数字比我想要的要大,因为getline()消耗了最后的换行符,但没有将其发送到输入流。

不过,我仍然不明白的是程序的输出。对于输入“Test\n”,为什么会得到“est\n”?为什么我的错误会影响字符串的第一个字符而不是在末尾添加不需要的垃圾?为什么程序的输出与调试器中字符串的外观不一致(“Test\n”,正如我所料)?

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

using namespace std;

int main()
{
    const int bufferSize = 1024;
    ifstream input( "test.txt", ios::in | ios::binary );

    vector<char> vecBuffer( bufferSize );
    input.getline( &vecBuffer[0], bufferSize );
    string strResult( vecBuffer.begin(), vecBuffer.begin() + input.gcount() );
    cout << strResult << "\n";

    return 0;
}
4

4 回答 4

12

我也复制了这个结果,Windows Vista、Visual Studio 2005 SP2。

当我弄清楚到底发生了什么,我会更新这篇文章。

编辑:好的,我们走了。问题(以及人们得到的不同结果)来自 \r. 发生的情况是您调用input.getline并将结果放入 vecBuffer。getline 函数去掉了 \n,但保留了 \r。

然后,您将 vecBuffer 转移到一个字符串变量,但使用输入中的 gcount 函数,这意味着您将获得太多的一个字符,因为输入变量仍然包含 \n,而 vecBuffer 没有。

结果 strResult 是:

-       strResult   "Test"
        [0] 84 'T'  char
        [1] 101 'e' char
        [2] 115 's' char
        [3] 116 't' char
        [4] 13 '␍'  char
        [5] 0   char

然后打印“Test”,然后是回车(将光标放回行首),空字符(覆盖 T),最后是 \n,它正确地将光标放在新行上.

因此,您要么必须去掉 \r,要么编写一个直接从 vecBuffer 获取字符串长度的函数,检查空字符。

于 2009-06-24T18:29:38.540 回答
6

我在 Windows XP Pro Service Pack 2 系统上复制了 Tommy 的问题,代码使用 Visual Studio 2005 SP2(实际上是“版本 8.0.50727.879”)编译为控制台项目。

如果我的 test.txt 文件只包含“Test”和一个 CR,则程序在运行时会输出“est”(注意前导空格)。

如果我不得不大胆猜测,我会说这个版本的实现有一个错误,它正在处理 Windows 换行符,就像它应该在 Unix 中处理一样(作为“转到同一行的前面”字符),然后它会清除第一个字符以保留下一个提示的一部分或其他内容。


更新: 玩了一会儿之后,我很肯定这就是正在发生的事情。如果您在调试器中查看 strResult,您会看到它在末尾复制了一个十进制 13 值。那是 CR,在 Windows 中是 '\n',其他地方都是“回到行首”。如果我改为将您的构造函数更改为:

字符串 strResult(vecBuffer.begin(), vecBuffer.begin() + input.gcount() - 1);

...(这样 CR 就不会被复制)然后它会像您期望的那样打印出“测试”。

于 2009-06-24T18:20:07.103 回答
2

我很确定 T 实际上被写入然后被覆盖。在 rxvt 窗口 (cygwin) 中运行相同的程序会产生预期的输出。你可以做几件事。如果您摆脱了打开中的 ios::binary ,它将自动将 \r\n 转换为 \n 并且事情会像您期望的那样工作。

您也可以在二进制编辑器中打开您的文本文件,方法是单击打开文件对话框的打开按钮上的小向下箭头并选择打开方式...->二进制编辑器。这将让您查看您的文件并确认它确实有 \r\n 而不仅仅是 \n。

编辑: 我将输出重定向到一个文件,它正在写出:

Test\r\0\r\n

你得到 \0 的原因是 gcount 返回 6 (从流中删除了 6 个字符),但最终的分隔符没有复制到缓冲区,而是一个 '\0' 。在构造字符串时,实际上是在告诉它包含“\0”。std::string 对嵌入的 0 没有问题,并按要求输出。一些 shell 显然输出了一个空白字符并覆盖了 T,而另一些则什么都不做,输出看起来还不错,但仍然可能是错误的,因为它具有嵌入的 '\0'

cout << strResult.c_str() << "\n";

将最后一行更改为此将在 \0 上停止并获得预期的输出。

于 2009-06-24T19:14:59.033 回答
1

我在 Windows XP Pro SP3(32 位)上使用 Visual Studio 2005 SP2 测试了您的代码,一切正常。

于 2009-06-24T18:04:53.383 回答