Почему std::ends приводит к сбою сравнения строк?

StackOverflow https://stackoverflow.com/questions/804826

  •  03-07-2019
  •  | 
  •  

Вопрос

Вчера я потратил около 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!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top