Почему std::ends приводит к сбою сравнения строк?
Вопрос
Вчера я потратил около 4 часов, пытаясь исправить эту проблему в своем коде.Я упростил задачу до приведенного ниже примера.
Идея состоит в том, чтобы сохранить строку в stringstream, заканчивающуюся 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 разной длины, даже если они имеют одинаковое содержимое.Более того, этот невидимый персонаж не обрезается с помощью функции boost trim.Однако, если вы используете strcmp для сравнения .c_str() двух строк, сравнение работает корректно.
Причина, по которой я использовал std::ends в первую очередь, заключается в том, что в прошлом у меня были проблемы со stringstream, сохранявшим мусорные данные в конце потока.std::ends решил это для меня.
Решение
std::ends
вставляет нулевой символ в поток.Получение контента в виде std::string
сохранит этот нулевой символ и создаст строку с этим нулевым символом в соответствующих позициях.
Таким образом, действительно, std::string может содержать встроенные нулевые символы.Следующий std::содержимое строки являются другой:
ABC
ABC\0
Двоичный ноль не является пробелом.Но он также не доступен для печати, поэтому вы его не увидите (если только ваш терминал не отобразит его специально).
Сравнение с использованием strcmp
будет интерпретировать содержание std::string
как строка C, когда вы передаете .c_str()
.В нем будет сказано
Хм, персонажи до первого
\0
(завершающий нулевой символ) являются Азбука, итак , я так понимаю, что строка является Азбука
И, таким образом, он не увидит никакой разницы между двумя вышеперечисленными.Вероятно, у вас возникла эта проблема:
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!
Также прочтите этот ответ: Как повторно использовать ostringstream
Другие советы
std::ends
это просто нулевой символ.Традиционно строки в C и C ++ заканчиваются нулевым символом (ascii 0), однако оказывается, что std::string
на самом деле эта штука не требуется.В любом случае, шаг за шагом просматривая ваш код, мы видим, что происходит несколько интересных вещей:
int main( int argc, char** argv )
{
Строковый литерал "hello"
является традиционной строковой константой, заканчивающейся нулем.Мы копируем это целое в std::string
здравствуйте.
const std::string HELLO( "hello" );
std::stringstream testStream;
Теперь мы поместим string
ПРИВЕТ (включая завершающий 0) в stream
, за которым следует второй null , который вводится туда вызовом 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 и hi выглядят одинаково, их .length() на самом деле отличается на 1.Это то, что я имею в виду предполагаю, что вызывает сбой оператора "==" .
Причина в том, что длина отличается на один завершающий нулевой символ.
Мой вопрос в том, почему.Я не понимаю, почему std::ends является невидимым символом, добавленным в строку hi, что делает hi и HELLO разными длины, даже если они имеют одинаковое содержимое.Более того, этот невидимый персонаж не получает дополнительной отделки.Однако, если вы используете strcmp для сравнения .c_str() двух строк, сравнение работает корректно.
strcmp
отличается от std::string
- он записан с самого начала, когда строки заканчивались нулем - поэтому, когда он доходит до первого конечного нуля в hi
он перестает смотреть.
Причина, по которой я использовал std::ends в во-первых, потому, что у меня были проблемы в прошлом со stringstream сохранение данных мусора в конце потока.std::ends решил это для меня.
Иногда хорошей идеей является понимание лежащего в основе представления.
Вы добавляете НУЛЕВОЙ символ в HELLO с помощью std::ends .Когда вы инициализируете hi с помощью str(), вы удаляете НУЛЕВОЙ символ.Струны разные.strcmp не сравнивает std::strings, он сравнивает char * (это функция C).
std::ends добавляет нулевой ограничитель (char)'\0'.Вы бы использовали его с устаревшими классами strstream, чтобы добавить нулевой ограничитель.
Вам это не нужно с stringstream, и на самом деле это все портит, потому что нулевой терминатор не является "специальным нулевым терминатором, который завершает строку" для stringstream, для stringstream это просто другой символ, нулевой символ.stringstream просто добавляет его, и это увеличивает количество символов (в вашем случае) до семи, и делает сравнение с "hello" неудачным.
Я думаю, что для того, чтобы иметь хороший способ сравнения строк, нужно использовать std::find
способ.Не смешивайте методы C и std::string ones
!