为啥我的字符串的开头消失了?

Posted

技术标签:

【中文标题】为啥我的字符串的开头消失了?【英文标题】:Why is the beginning of my string disappearing?为什么我的字符串的开头消失了? 【发布时间】:2009-06-24 16:59:07 【问题描述】:

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

不过,我仍然不明白的是程序的输出。对于输入“Test\n”,为什么会得到“est\n”?为什么我的错误会影响字符串的 first 字符,而不是在末尾添加不需要的垃圾?为什么程序的输出与调试器中字符串的显示方式不一致(“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;

【问题讨论】:

在 RHEL4 上使用 g++ 3.4.3 为我工作。你的平台是什么? 对我来说也是如此:MinGW/MSYS (Windows XP) 上的 g++ 3.4.5 我在 Windows XP 上使用 Visual Studio 2005。这可能与我在 Windows 环境中创建测试文件的事实有关,因此换行符由两个字符组成? 尝试确定“vecBuffer.begin()”返回的内容。我知道这很愚蠢,但它看起来像是一个错误,这就是你的“一个”正在运行的地方。 对 vecBuffer.begin() 的两次调用都返回 84 ('T')。 【参考方案1】:

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

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

edit:好的,我们开始吧。问题(以及人们得到的不同结果)来自 \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 获取字符串长度,检查空字符。

【讨论】:

优秀。感谢您的解释。这是我开始怀疑的。剩下的唯一问题是为什么 Naaff 的结果与我的和 T.E.D. 的结果不同。 不确定,也许他错过了 Enter 键并最终得到一个没有换行符的文件?我的第一个猜测是他使用了一个使用 Linux 样式换行符的编辑器,但他说他使用的是记事本,所以除非 XP SP3 改变了记事本的行为...... 我认为仅剥离一个终结器的问题。我在 emacs hexl-mode 中检查了文本文件,它以 CRLF 组合结尾。末尾的 LF 没有读入,但显然 CR 是。 正如 T.E.D. 的回答所述,我没有在控制台项目中进行测试,所以我怀疑这可能是我没有看到问题的原因。 问题是 0 得到输出,控制台用空白字符覆盖了 T,而其他一些 shell 什么也没做。 strResult.length()==6【参考方案2】:

我在 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',其他地方都是“回到行首”。如果我改为将您的构造函数更改为:

string strResult(vecBuffer.begin(), vecBuffer.begin() + input.gcount() - 1);

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

【讨论】:

这听起来很有希望。换行符根本不应该进入字符串,但也许记事本的 CR+LF 换行符被解释为一个奇怪的指令,后跟一个换行符。奇怪的指令必须是走到前面,然后打印一个空格 - 请参阅 cmets 对 Naaff 的回答。想知道为什么他不能繁殖吗? 我认为我无法重现该问题,因为我没有将您的代码放入控制台项目中——我只是添加了一些我正在处理的代码,并没有想到它会很重要。 我在 Windows 上不止一次遇到过这种情况。每次我盯着我的显示器花了很长时间才想起发生了什么。 我用emacs创建文件,所以不能怪记事本。 input.gcount-1 仍然复制\r,但跳过\0(见我的回答)【参考方案3】:

我很确定 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 处停止,并获得预期的输出。

【讨论】:

谢谢,海豚。我们中的很多人一直假设 gcount() 返回 5。我不知道为什么 - 现在您已经指出它似乎很明显会是 6。使用我发布的代码无法看出从 gcount 的结果中减去 1 和减去 2 之间的区别,但 Naaff 建议的对 cout 行的更改显示了这一点。【参考方案4】:

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

【讨论】:

有趣。你是如何创建测试文件的?我用记事本++。 记事本。输入“Test”,然后按回车键并保存“test.txt”。 当你把你的 cout 行改成这样会发生什么?: cout 奇怪:我只是做了同样的事情,它吐出了“est”。我安装 Visual Studio 的“关于”框在版本号后面有“(SP.050727-7600)”。所以我想我没有安装SP2。你觉得会是这样吗? 哦,等一下,我会试试你的建议 - 我在评论之前没有更新。

以上是关于为啥我的字符串的开头消失了?的主要内容,如果未能解决你的问题,请参考以下文章

为啥字符串在Java中以“”开头? [复制]

如果文本太长,为啥 Snackbar 文本会消失?

为啥闪烁自动完成会离开单词的开头

为啥我的字符串分配会导致分段错误?

为啥使用 QString::right 在字符串的开头省略逗号?

安卓/数据库。知道为啥电话号码中的第一个字符“+”会消失吗?