为什么std :: ends导致字符串比较失败?

时间:2009-04-29 23:53:58

标签: c++ stl boost

我昨天花了大约4个小时试图在我的代码中解决这个问题。我将问题简化为以下示例。

这个想法是将字符串存储在以std :: ends结尾的字符串流中,然后稍后检索它并将其与原始字符串进行比较。

#include <sstream>
#include <iostream>
#include <string>

int main( int argc, char** argv )
{
    const std::string HELLO( "hello" );

    std::stringstream testStream;

    testStream << HELLO << std::ends;

    std::string hi = testStream.str();

    if( HELLO == hi )
    {
        std::cout << HELLO << "==" << hi << std::endl;
    }

    return 0;
}

正如您可能猜到的,执行上述代码时不会打印任何内容。

虽然,如果打印出来,或者在调试器(VS2005)中查看,HELLO和hi看起来相同,他们的.length()实际上相差1.这就是我猜测导致“==”运算符失败。

我的问题是为什么。我不明白为什么std :: ends是一个不可见的字符添加到字符串hi,使hi和HELLO的长度不同,即使它们具有相同的内容。此外,这个看不见的角色不会被增强修剪修剪。但是,如果使用strcmp比较两个字符串的.c_str(),则比较正常。

我首先使用std :: ends的原因是因为我在过去遇到了一些问题,因为stringstream在流的末尾保留了垃圾数据。 std :: ends为我解决了这个问题。

5 个答案:

答案 0 :(得分:11)

std::ends在流中插入一个空字符。将内容作为std::string保留将保留该空字符,并在相应位置创建一个具有该空字符的字符串。

所以std :: string确实可以包含嵌入的空字符。以下std :: string内容 不同:

ABC
ABC\0

二进制零不是空格。但它也不能打印,所以你不会看到它(除非你的终端专门显示它)。

使用strcmp进行比较会在您传递std::string时将.c_str()的内容解释为C字符串。它会说

  

嗯,第一个\0(终止空字符)之前的字符是 ABC ,所以我认为字符串是 ABC

因此,上述两者之间不会有任何区别。您可能遇到此问题:

std::stringstream s;
s << "hello";
s.seekp(0);
s << "b";
assert(s.str() == "b"); // will fail!

断言将失败,因为stringstream使用的序列仍然是包含“hello”的旧序列。你做的只是覆盖第一个角色。你想这样做:

std::stringstream s;
s << "hello";
s.str(""); // reset the sequence
s << "b";
assert(s.str() == "b"); // will succeed!

另请阅读此答案:How to reuse an ostringstream

答案 1 :(得分:4)

std::ends只是一个空字符。传统上,C和C ++中的字符串以null(ascii 0)字符终止,但事实证明std::string并不真正需要这个东西。无论如何,我们逐点逐步完成您的代码,我们会看到一些有趣的事情:

int main( int argc, char** argv )
{

字符串文字"hello"是传统的零终止字符串常量。我们将整个复制到std::string HELLO。

    const std::string HELLO( "hello" );

    std::stringstream testStream;

我们现在将string HELLO(包括尾随0)放入stream,然后是第二个空值,通过调用std::ends放置。

    testStream << HELLO << std::ends;

我们提取出我们放入stream的内容的副本(文字字符串“hello”,加上两个空终止符)。

    std::string hi = testStream.str();

然后我们使用operator ==类上的std::string比较两个字符串。此运算符(可能)比较string对象的长度 - 包括多少尾随空字符。请注意,std::string类不要求基础字符数组以尾随空格结尾 - 换句话说它允许字符串包含空字符,因此两个尾随空字符中的第一个被视为字符串的一部分hi

由于两个字符串的尾随空值数不同,因此比较失败。

    if( HELLO == hi )
    {
        std::cout << HELLO << "==" << hi << std::endl;
    }

    return 0;
}
  

虽然,如果打印出来,或者看着   在调试器(VS2005)中,HELLO和嗨   看起来相同,他们的.length()in   事实相差1.这就是我   猜测导致“==”运算符   失败。

原因是,长度因尾随空字符而不同。

  

我的问题是为什么。我不   理解为什么std :: ends是一个   不可见的字符添加到字符串   嗨,喜欢和你好不同   即使他们有长度   相同的内容。而且,这个   看不见的角色没有得到   用增强装饰修剪。但是,如果   你使用strcmp来比较.c_str()   两个字符串,比较工作   正确。

strcmpstd::string不同 - 它是在字符串以null结尾的早期编写的 - 所以当它到达hi中的第一个尾随空值时停止寻找。

  

我使用std ::的原因结束了   第一名是因为我遇到了问题   在过去使用stringstream   保留垃圾数据   流。 std :: ends解决了这个问题   我

有时理解底层表示是个好主意。

答案 2 :(得分:0)

您正在使用std :: ends向HELLO添加NULL char。使用str()初始化hi时,您将删除NULL char。字符串是不同的。 strcmp不比较std :: strings,它比较char *(它是一个C函数)。

答案 3 :(得分:0)

std :: ends添加一个空终止符,(char)'\ 0'。您将它与已弃用的strstream类一起使用,以添加null终止符。

你不需要使用stringstream,事实上它搞砸了,因为null终止符不是“结束字符串的特殊空终结符”到字符串流,字符串流它只是另一个字符,第0个字符。 stringstream只是添加它,并且会将字符数(在您的情况下)增加到7,并使比较为“hello”失败。

答案 4 :(得分:0)

我认为有一种比较字符串的好方法是使用std::find方法。不要混用C方法和std::string ones