Pregunta

Ayer pasé unas 4 horas tratando de solucionar este problema en mi código. Simplifiqué el problema con el siguiente ejemplo.

La idea es almacenar una cadena en una secuencia de cadenas que termina con std :: ends, luego recuperarla y compararla con la cadena original.

#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;
}

Como probablemente pueda adivinar, el código anterior cuando se ejecute no imprimirá nada.

Aunque, si se imprime o se ve en el depurador (VS2005), HOLA y hola se ven idénticos, su longitud () (de hecho) difiere de hecho en 1. Eso es lo que supongo que está causando el " == " Operador a fallar.

Mi pregunta es por qué. No entiendo por qué std :: ends es un carácter invisible que se agrega a la cadena hi, lo que hace que hi y HELLO tengan diferentes longitudes aunque tengan un contenido idéntico. Por otra parte, este personaje invisible no se recorta con el recorte de refuerzo. Sin embargo, si utiliza strcmp para comparar .c_str () de las dos cadenas, la comparación funciona correctamente.

La razón por la que utilicé std :: final en primer lugar es porque he tenido problemas en el pasado con cadenas de datos que retienen datos de basura al final de la secuencia. std :: termina resuelto eso para mi.

¿Fue útil?

Solución

std :: ends inserta un carácter nulo en la secuencia. Obtener el contenido como std :: string retendrá ese carácter nulo y creará una cadena con ese carácter nulo en las posiciones respectivas.

De hecho, una cadena std :: puede contener caracteres nulos incrustados. Los siguientes std :: string contents son diferentes:

ABC
ABC\0

Un cero binario no es un espacio en blanco. Pero tampoco es imprimible, por lo que no lo verá (a menos que su terminal lo muestre especialmente).

La comparación con strcmp interpretará el contenido de std :: string como una cadena en C cuando pase .c_str () . Dirá

  

Hmm, los caracteres antes del primer \ 0 (terminando el carácter nulo) son ABC , así que supongo que la cadena es ABC

Y por lo tanto, no verá ninguna diferencia entre los dos anteriores. Probablemente tenga este problema:

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

La afirmación fallará, porque la secuencia que utiliza el flujo de cadena sigue siendo la antigua que contiene "hola". Lo que hiciste es solo sobrescribir el primer personaje. Quieres hacer esto:

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

Lea también esta respuesta: Cómo reutilizar un ostringstream

Otros consejos

std :: ends es simplemente un carácter nulo. Tradicionalmente, las cadenas en C y C ++ terminan con un carácter nulo (ascii 0), sin embargo, resulta que std :: string realmente no requiere esto. De todos modos, para revisar su código punto por punto, vemos algunas cosas interesantes que suceden:

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

La cadena literal " hola " es una constante de cadena terminada en cero tradicional. Copiamos todo eso en std :: string HELLO.

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

    std::stringstream testStream;

Ahora colocamos la cadena HELLO (incluido el 0 final) en la secuencia , seguida de un segundo nulo que se agrega a la llamada a std :: termina .

    testStream << HELLO << std::ends;

Extraemos una copia de las cosas que colocamos en el stream (la cadena literal " hola " más los dos terminadores nulos).

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

Luego comparamos las dos cadenas utilizando el operator == en la clase std :: string . Este operador (probablemente) compara la longitud de los objetos string , incluida la cantidad de caracteres nulos finales. Tenga en cuenta que la clase std :: string no requiere que la matriz de caracteres subyacente finalice con un valor nulo al final: en otras palabras, permite que la cadena contenga caracteres nulos, de modo que el primero de los dos caracteres nulos finales es tratado como parte de la cadena hi .

Dado que las dos cadenas son diferentes en el número de nulos finales, la comparación falla.

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

    return 0;
}
  

Aunque, si está impreso, o mirado   en el depurador (VS2005), HELLO y hola   parecen idénticos, su .length () en   el hecho difiere por 1. Eso es lo que soy   adivinar está causando que " == " operador   fallar.

La razón es que la longitud es diferente por un carácter nulo final.

  

Mi pregunta es por qué. Yo no   Entender porque std :: ends es un   carácter invisible añadido a la cadena   hola, hola y hola diferente   Longitudes a pesar de que tienen   contenido idéntico Además, esta   personaje invisible no consigue   recortado con refuerzo de recorte. Sin embargo, si   usa strcmp para comparar .c_str () de   Las dos cuerdas, la comparación funciona.   correctamente.

strcmp es diferente de std :: string : está escrito desde los primeros días en que las cadenas terminaron con un nulo, por lo que cuando llega a la primera al final de nulo en hi deja de mirar.

  

La razón por la que usé std :: termina en el   El primer lugar es porque he tenido problemas   en el pasado con stringstream   reteniendo datos de basura al final de   la corriente. std :: termina resuelto que para   yo.

A veces es una buena idea entender la representación subyacente.

Estás agregando un carácter NULL a HOLA con std :: ends. Cuando inicializa hi con str () está eliminando el carácter NULL. Las cuerdas son diferentes. strcmp no compara std :: strings, compara char * (es una función de C).

std :: ends agrega un terminador nulo, (char) '\ 0'. Lo usaría con las clases strstream en desuso, para agregar el terminador nulo.

No lo necesitas con stringstream y, de hecho, se complica, porque el terminador nulo no es " el terminador nulo especial que termina una cadena " a stringstream, a stringstream es solo otro carácter, el carácter cero. stringstream simplemente lo agrega, y eso aumenta el número de caracteres (en tu caso) a siete, y hace la comparación a " hello " fallar.

Creo que tener una buena forma de comparar cadenas es usar el método std :: find . ¡No mezcle los métodos C y std :: string ones !

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top