Почему std :: end приводит к сбою сравнения строк? - PullRequest
0 голосов
/ 30 апреля 2009

Я провел около 4 часов вчера, пытаясь исправить эту проблему в своем коде. Я упростил задачу до приведенного ниже примера.

Идея состоит в том, чтобы сохранить строку в потоке строки, заканчивающемся std :: end, а затем извлечь ее позже и сравнить с исходной строкой.

#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 :: end является невидимым символом, добавляемым в строку hi, делая hi и HELLO разной длины, даже если они имеют идентичное содержимое. Более того, этот невидимый персонаж не будет урезан бустом. Однако, если вы используете strcmp для сравнения .c_str () двух строк, сравнение работает правильно.

Причина, по которой я в первую очередь использовал std :: окончание, заключается в том, что в прошлом у меня были проблемы с сохранением данных мусора в потоке строки в конце потока. STD :: Концы решили это для меня.

Ответы [ 5 ]

11 голосов
/ 30 апреля 2009

std::ends вставляет нулевой символ в поток. Получение содержимого как std::string сохранит этот нулевой символ и создаст строку с этим нулевым символом в соответствующих позициях.

Так что действительно, std :: string может содержать встроенные нулевые символы. Следующее содержимое std :: string отличается от :

ABC
ABC\0

Бинарный ноль не является пробелом. Но это также не для печати, так что вы не увидите его (если ваш терминал отображает его специально).

Сравнение с использованием strcmp будет интерпретировать содержимое std::string как строку C, когда вы передадите .c_str(). Это скажет

Хм, символы перед первым \0 (завершающим нулевым символом) равны ABC , поэтому я понимаю, что строка ABC

И поэтому он не увидит никакой разницы между двумя выше. Вы, вероятно, имеете эту проблему:

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

Утверждение не удастся, потому что последовательность, используемая потоком строк, все еще старая, которая содержит «привет». То, что вы сделали, это просто переписали первый символ. Вы хотите сделать это:

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

Также прочитайте этот ответ: Как повторно использовать поток ostring

4 голосов
/ 30 апреля 2009

std::ends - это просто нулевой символ. Традиционно строки в C и C ++ заканчиваются нулевым символом (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 (буквенная строка «привет», плюс два нулевых терминатора).

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

Затем мы сравниваем две строки, используя operator == в классе std::string. Этот оператор (вероятно) сравнивает длину string объектов - включая количество конечных нулевых символов. Обратите внимание, что класс std::string не требует, чтобы базовый массив символов заканчивался завершающим нулем - другими словами, он позволяет строке содержать нулевые символы, поэтому первый из двух конечных нулевых символов обрабатывается как часть строки hi.

Поскольку две строки различаются по количеству завершающих нулей, сравнение не удается.

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

    return 0;
}

Хотя, если распечатать или посмотреть на в отладчике (VS2005), привет и привет выглядят одинаково, их .length () в факт отличается на 1. Это то, что я угадывание вызывает оператор "==" потерпеть неудачу.

Причина в том, что длина отличается на один завершающий нулевой символ.

Мой вопрос - почему? я не буду понять, почему STD :: заканчивается невидимый символ добавлен в строку привет, привет и привет длины, даже если у них есть идентичный контент. Более того, это невидимый персонаж не получает отделан бустом Однако если вы используете strcmp для сравнения .c_str () из две строки, сравнение работает правильно.

strcmp отличается от std::string - он записывается в первые дни, когда строки заканчивались нулем - поэтому, когда он достигает первого завершающего нуля в hi, он перестает смотреть.

Причина, по которой я использовал std :: заканчивается в первое место, потому что у меня были проблемы в прошлом с потоком строк сохранение данных мусора в конце поток. STD :: заканчивается решено, что для я.

Иногда полезно понять базовое представление.

0 голосов
/ 07 октября 2010

Я думаю, что для сравнения строк лучше использовать метод std::find. Не смешивайте методы C и std::string ones!

0 голосов
/ 30 апреля 2009

std :: заканчивается добавляет нулевой терминатор (char) '\ 0'. Вы бы использовали его с устаревшими классами strstream, чтобы добавить нулевой терминатор.

Вам не нужно это с помощью stringstream, и на самом деле это все портит, потому что нулевой терминатор не является «специальным нулевым терминатором, заканчивающим строку» для stringstream, для stringstream это просто еще один символ, нулевой символ , stringstream просто добавляет его, и это увеличивает количество символов (в вашем случае) до семи и делает сравнение с «hello» неудачным.

0 голосов
/ 30 апреля 2009

Вы добавляете в HELLO NULL char с помощью std :: заканчивается. Когда вы инициализируете hi с помощью str (), вы удаляете NULL char. Строки разные. strcmp не сравнивает std :: strings, он сравнивает char * (это функция C).

...