Question

J'ai passé environ 4 heures hier à essayer de résoudre ce problème dans mon code. J'ai simplifié le problème à l'exemple ci-dessous.

L'idée est de stocker une chaîne dans un stringstream se terminant par std :: ends, puis de la récupérer plus tard et de la comparer à la chaîne d'origine.

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

Comme vous pouvez probablement le deviner, le code ci-dessus, une fois exécuté, n’affiche rien.

Bien que, s'ils sont imprimés ou consultés dans le débogueur (VS2005), HELLO et hi semblent identiques, leur .length () diffère en réalité de 1. C’est ce que je suppose qui est à l’origine du " == " opérateur à échouer.

Ma question est pourquoi. Je ne comprends pas pourquoi std :: ends est un caractère invisible ajouté à string hi, rendant hi et HELLO de longueurs différentes même si leur contenu est identique. De plus, ce personnage invisible ne reçoit pas de rognage boost. Cependant, si vous utilisez strcmp pour comparer .c_str () des deux chaînes, la comparaison fonctionne correctement.

La raison pour laquelle j’ai utilisé std :: ends à la base, c’est parce que j’ai eu des problèmes avec le fait que stringstream conserve les données parasites à la fin du flux. std :: ends a résolu ça pour moi.

Était-ce utile?

La solution

std :: ends insère un caractère null dans le flux. Obtenir le contenu en tant que std :: string conservera ce caractère nul et créera une chaîne avec ce caractère nul aux positions respectives.

Donc, en effet, std :: string peut contenir des caractères null incorporés. Le contenu std :: string suivant est différent:

ABC
ABC\0

Un zéro binaire n'est pas un espace. Mais ce n'est pas non plus imprimable, vous ne le verrez donc pas (à moins que votre terminal ne l'affiche spécialement).

La comparaison à l'aide de strcmp interprétera le contenu d'un std :: string en tant que chaîne C lorsque vous transmettez .c_str () . Il va dire

  

Hmm, les caractères avant le premier \ 0 (caractère nul final) sont ABC , donc je suppose que la chaîne est ABC

Et ainsi, il ne verra aucune différence entre les deux ci-dessus. Vous avez probablement ce problème:

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

L’assertion échouera, car la séquence utilisée par le stringstream est toujours l’ancienne qui contient "hello". Vous avez simplement écrasé le premier caractère. Vous voulez faire ceci:

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

Lisez également cette réponse: Comment réutiliser un flux ostring

Autres conseils

std :: ends est simplement un caractère nul. Traditionnellement, les chaînes en C et C ++ se terminent par un caractère nul (ascii 0), mais il s'avère que std :: string ne nécessite pas vraiment cette chose. Quoi qu'il en soit, pour parcourir votre code point par point, nous voyons quelques choses intéressantes se dérouler:

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

Le littéral de chaîne "hello" est une constante de chaîne terminée par un zéro. Nous copions ce tout dans le std :: string BONJOUR.

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

    std::stringstream testStream;

Nous plaçons maintenant la chaîne HELLO (y compris le 0 de fin) dans le flux , suivi d'un second null qui y est placé par l'appel de std :: ends .

    testStream << HELLO << std::ends;

Nous extrayons une copie des éléments que nous avons insérés dans le flux (la chaîne littérale "hello", plus les deux terminateurs nuls).

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

Nous comparons ensuite les deux chaînes en utilisant l'opérateur operator == de la classe std :: string . Cet opérateur (probablement) compare la longueur des objets string - y compris le nombre de caractères nuls de fin. Notez que la classe std :: string ne nécessite pas que le tableau de caractères sous-jacent se termine par un null final - autrement dit, il permet à la chaîne de contenir des caractères nuls de sorte que le premier des deux derniers caractères nuls soit traité comme faisant partie de la chaîne hi .

Étant donné que le nombre de valeurs de fin de chaîne est différent pour les deux chaînes, la comparaison échoue.

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

    return 0;
}
  

Bien que, si imprimé, ou regardé   dans le débogueur (VS2005), BONJOUR et bonjour   semblent identiques, leur .length () dans   le fait diffère de 1. C'est ce que je suis   deviner est la cause du " == " opérateur   échouer.

Raison d'être, la longueur est différente d'un caractère nul final.

  

Ma question est pourquoi. Non   comprendre pourquoi std :: ends est un   caractère invisible ajouté à la chaîne   salut, faire salut et bonjour différent   longueurs même si elles ont   contenu identique. De plus, cela   personnage invisible ne reçoit pas   garni avec boost trim. Toutefois, si   vous utilisez strcmp pour comparer .c_str () de   les deux chaînes, la comparaison fonctionne   correctement.

strcmp est différent de std :: string - il est écrit depuis les premiers jours où les chaînes étaient terminées par un null - alors quand il arrive au premier à la fin de null dans salut , la recherche s’arrête.

  

La raison pour laquelle j'ai utilisé std :: end dans le   la première place est parce que j'ai eu des problèmes   dans le passé avec stringstream   la conservation des données de déchets à la fin de   le flux. std :: ends résolus que pour   moi.

Parfois, il est judicieux de comprendre la représentation sous-jacente.

Vous ajoutez un caractère NULL à HELLO avec std :: ends. Lorsque vous initialisez hi avec str (), vous supprimez le caractère NULL. Les cordes sont différentes. strcmp ne compare pas std :: strings, il compare char * (c'est une fonction C).

std :: ends ajoute un terminateur nul, (char) '\ 0'. Vous l'utiliseriez avec les classes strstream obsolètes pour ajouter le terminateur null.

Vous n'en avez pas besoin avec stringstream. En fait, cela gâche tout, car le terminateur null n'est pas le "terminateur nul spécial qui termine une chaîne". stringstream, stringstream c'est juste un autre caractère, le caractère zéro. stringstream l'a simplement ajouté, ce qui augmente le nombre de caractères (dans votre cas) à sept et effectue la comparaison avec "hello". échouer.

Je pense avoir un bon moyen de comparer des chaînes est d'utiliser la méthode std :: find . Ne mélangez pas les méthodes C et celles de std :: string !

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top