Por que std :: extremidades causa comparação de string a falhar?
Pergunta
passei cerca de 4 horas ontem tentando corrigir esse problema no meu código. I simplificou o problema para o exemplo abaixo.
A idéia é armazenar uma string em um stringstream terminando com std :: extremidades, depois recuperá-lo mais tarde e compará-la com a string 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 você provavelmente pode adivinhar, o código acima quando executado não irá imprimir nada.
Embora, se impresso, ou olhou no depurador (VS2005), OLÁ e olhar oi idêntica, sua .length () Na realidade difere por 1. Isso é o que eu estou supondo que está causando o operador "==" para falhou.
A minha pergunta é o porquê. Eu não entendo por que std :: extremidades é um personagem invisível adicionado a corda oi, tornando oi e Olá comprimentos diferentes, embora tenham conteúdo idêntico. Além disso, este personagem invisível não se aparadas com impulso da guarnição. No entanto, se você usar strcmp para comparar .c_str () das duas cordas, a comparação funciona corretamente.
A razão que eu usei std :: extremidades, em primeiro lugar é porque eu tive problemas no passado com dados de lixo stringstream de retenção no final do fluxo. std :: extremidades resolvido isso para mim.
Solução
std::ends
insere um caractere nulo no fluxo. Obtendo o conteúdo como um std::string
vai manter esse caractere nulo e criar uma cadeia com que caractere nulo nas respectivas posições.
caracteres nulos Então, na verdade, um std :: string pode conter incorporado. O conteúdo seguinte std :: string são diferente:
ABC
ABC\0
Um zero binário não espaço em branco é. Mas também não é impressão, para que você não vai vê-lo (a menos que seus monitores de terminais que ele seja especialmente).
Comparando usando strcmp
irá interpretar o conteúdo de um std::string
como uma string C quando você passa .c_str()
. Ele vai dizer
Hmm, caracteres antes do primeiro
\0
(terminação caractere nulo) são ABC , então eu levá-la a corda é ABC
E, assim, ele não vai ver nenhuma diferença entre os dois acima. Você provavelmente está tendo esse problema:
std::stringstream s;
s << "hello";
s.seekp(0);
s << "b";
assert(s.str() == "b"); // will fail!
O assert irá falhar, porque a seqüência que usa o stringstream ainda é o antigo que contém "Olá". O que você fez é apenas substituindo o primeiro caractere. Você quer fazer isso:
std::stringstream s;
s << "hello";
s.str(""); // reset the sequence
s << "b";
assert(s.str() == "b"); // will succeed!
Leia também esta resposta: Como reutilizar um ostringstream
Outras dicas
std::ends
é simplesmente um caractere nulo. Tradicionalmente, cordas em C e C ++ terminam com um caractere nulo (ASCII 0), no entanto verifica-se que std::string
realmente não exigem essa coisa. De qualquer forma a passo através de seu ponto de código de ponto vemos algumas coisas interessantes acontecendo:
int main( int argc, char** argv )
{
O "hello"
literal string é uma tradicional terminado constante de cadeia zero. Copiamos que todo na OLÁ std::string
.
const std::string HELLO( "hello" );
std::stringstream testStream;
agora colocar o OlÁ string
(incluindo o arrasto 0) para o stream
, seguido por um segundo nulo que é colocada ali pela chamada para std::ends
.
testStream << HELLO << std::ends;
Nós extrair uma cópia do material que colocamos no stream
(a string literal "Olá", mais os dois terminadores nulos).
std::string hi = testStream.str();
Em seguida, comparar as duas seqüências usando o operator ==
na classe std::string
. Este operador (provavelmente) compara o comprimento dos objetos string
- incluindo como nunca caracteres nulos muitos de fuga. Note-se que a classe std::string
não exige que o array de caracteres subjacente para terminar com um nulo à direita - dito de outra forma ele permite que a corda para conter caracteres nulos para que o primeiro dos dois à direita caracteres nulos é tratada como parte do hi
corda
Uma vez que as duas cadeias são diferentes do número de valores nulos de arrasto, a comparação falha.
if( HELLO == hi )
{
std::cout << HELLO << "==" << hi << std::endl;
}
return 0;
}
Embora, se impresso, ou olhou no depurador (VS2005), OlÁ e oi parecem idênticos, sua .length () in fato difere por 1. Isso é o que eu sou supondo que está causando o operador "==" a falhar.
Razão de ser, o comprimento é diferente por um caractere nulo final.
A minha pergunta é o porquê. eu não entender por que std :: extremidades é um personagem invisível adicionado a corda oi, tornando oi e OLÁ diferente comprimentos, apesar de terem conteúdo idêntico. Além disso, este personagem invisível não recebe aparadas com impulso da guarnição. No entanto, se você usa strcmp para comparar .c_str () de as duas cordas, as obras de comparação corretamente.
strcmp
é diferente de std::string
- está escrito de volta nos primeiros dias, quando cordas foram encerrados com um nulo -. Assim, quando se chega ao primeiro nulo à direita no hi
ele pára de olhar
A razão que eu usei std :: extremidades na primeiro lugar é porque eu tive problemas no passado com stringstream retendo dados de lixo na extremidade de o fluxo. std :: extremidades resolvido que, para me.
Às vezes é uma boa idéia para compreender a representação subjacente.
Você está adicionando um char NULL para OLÁ com std :: extremidades. Quando você inicializa oi com str () você está removendo o caractere NULL. As cordas são diferentes. não strcmp não comparar std :: cordas, ele compara char * (é uma função C).
std :: extremidades adiciona um terminador nulo, (char) '\ 0'. Você poderia usá-lo com as classes strstream obsoletos, para adicionar o terminador nulo.
Você não precisa dele com stringstream, e na verdade ele parafusos as coisas, porque o terminador nulo não é "o terminador nulo especial que termina a string" para stringstream, para stringstream é só outro personagem, o personagem zeroth . stringstream apenas acrescenta, e que aumenta a contagem de caracteres (no seu caso) para sete, e faz a comparação com "Olá" falhar.
Eu acho que ter uma boa maneira de comparar strings é usar o método std::find
. Não misture métodos C e std::string ones
!