代码之家  ›  专栏  ›  技术社区  ›  Tommy Herbert

为什么我的线头消失了?

  •  6
  • Tommy Herbert  · 技术社区  · 17 年前

    在下面的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 回复  |  直到 17 年前
        1
  •  12
  •   Aistina    17 年前

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

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

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

    然后将vecbuffer传输到字符串变量,但从input使用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获取字符串长度的函数,检查是否有空字符。

        2
  •  6
  •   T.E.D.    17 年前

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

    如果test.txt文件只包含“test”和CR,那么程序在运行时会发出“est”(注意前导空格)。

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


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

    字符串strresult(vecBuffer.begin(),vecBuffer.begin()+input.gCount()-1);

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

        3
  •  2
  •   Dolphin    17 年前

    我很确定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,而其他shell则不做任何操作,输出看起来正常,但可能仍然是错误的,因为它嵌入了'\0'

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

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

        4
  •  1
  •   Naaff    17 年前

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