Question

Si je veux construire un std :: string avec une ligne comme:

std::string my_string("a\0b");

Là où je veux avoir trois caractères dans la chaîne résultante (a, null, b), je n’en ai qu’un. Quelle est la syntaxe appropriée?

Était-ce utile?

La solution

Depuis C ++ 14

nous avons pu créer un littéral std::string

#include <iostream>
#include <string>

int main()
{
    using namespace std::string_literals;

    std::string s = "pl-\0-op"s;    // <- Notice the "s" at the end
                                    // This is a std::string literal not
                                    // a C-String literal.
    std::cout << s << "\n";
}

Avant C ++ 14

Le problème est le constructeur const char* qui prend un \0 en supposant que l'entrée est une chaîne de caractères. Les chaînes de caractères sont c_str() terminées et l'analyse s'arrête dès lors qu'elle atteint le caractère vector<char>.

Pour compenser cela, vous devez utiliser le constructeur qui construit la chaîne à partir d'un tableau de caractères (pas un C-String). Cela prend deux paramètres - un pointeur sur le tableau et une longueur:

std::string   x("pq\0rs");   // Two characters because input assumed to be C-String
std::string   x("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.

Remarque: C ++ <=> est PAS <=> - terminé (comme suggéré dans d'autres publications). Cependant, vous pouvez extraire un pointeur vers un tampon interne contenant une chaîne de caractères avec la méthode <=>.

Découvrez également la réponse de Doug T ci-dessous sur l’utilisation d’un <=>.

Vérifiez également la RiaD pour une solution C ++ 14.

Autres conseils

Si vous faites une manipulation comme vous le feriez avec une chaîne de style c (tableau de caractères), envisagez d'utiliser

std::vector<char>

Vous avez plus de liberté pour le traiter comme un tableau de la même manière que vous traiteriez une chaîne de caractères. Vous pouvez utiliser copy () pour copier dans une chaîne:

std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());

et vous pouvez l'utiliser dans beaucoup des mêmes endroits que vous pouvez utiliser des c-strings

printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';

Naturellement, cependant, vous rencontrez les mêmes problèmes que les c-strings. Vous pouvez oublier votre terminal nul ou écrire au-delà de l'espace alloué.

Je ne sais pas pourquoi vous voudriez faire une telle chose, mais essayez ceci:

std::string my_string("a\0b", 3);

Quelles nouvelles fonctionnalités les littéraux définis par l'utilisateur ajoutent-ils au C ++? apporte une réponse élégante: Définir

std::string operator "" _s(const char* str, size_t n) 
{ 
    return std::string(str, n); 
}

alors vous pouvez créer votre chaîne de cette façon:

std::string my_string("a\0b"_s);

ou même ainsi:

auto my_string = "a\0b"_s;

Il y a un " ancien style " manière:

#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string

alors vous pouvez définir

std::string my_string(S("a\0b"));

Ce qui suit fonctionnera ...

std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');

Vous devrez faire attention à cela. Si vous remplacez "b" par un caractère numérique, vous créerez la chaîne incorrecte en mode silencieux à l'aide de la plupart des méthodes. Voir: Règles pour le caractère d'échappement des littéraux de chaîne.

Par exemple, j'ai laissé tomber cet extrait innocent au milieu d'un programme

// Create '\0' followed by '0' 40 times ;)
std::string str("\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", 80);
std::cerr << "Entering loop.\n";
for (char & c : str) {
    std::cerr << c;
    // 'Q' is way cooler than '\0' or '0'
    c = 'Q';
}
std::cerr << "\n";
for (char & c : str) {
    std::cerr << c;
}
std::cerr << "\n";

Voici ce que ce programme a généré pour moi:

Entering loop.
Entering loop.

vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ

Ce fut ma première déclaration d'impression deux fois, plusieurs caractères non imprimables, suivis d'une nouvelle ligne, suivie de quelque chose dans la mémoire interne, que je viens d'écraser (puis d'imprimer, montrant qu'elle a été écrasée). Le pire de tout, même en compilant cela avec minutieux et bavard gcc warnings ne m'a donné aucune indication quant à quelque chose qui n'allait pas, et l'exécution du programme par Valgrind ne se plaignait d'aucun modèle d'accès mémoire incorrect. En d'autres termes, il est complètement indétectable par les outils modernes.

Vous pouvez obtenir le même problème avec le beaucoup plus simple std::string("0", 100);, mais l'exemple ci-dessus est un peu plus compliqué, et donc plus difficile à comprendre ce qui ne va pas.

Heureusement, C ++ 11 nous offre une bonne solution au problème en utilisant la syntaxe de la liste d’initialisation. Cela vous évite d'avoir à spécifier le nombre de caractères (ce que, comme je l'ai montré ci-dessus, vous pouvez le faire incorrectement), et évite de combiner des nombres échappés. std::string str({'a', '\0', 'b'}) est sûr pour tout contenu de chaîne, contrairement aux versions qui utilisent un tableau de char et une taille.

En C ++ 14, vous pouvez maintenant utiliser des littéraux

using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3

Mieux vaut utiliser std :: vector < char > si cette question ne sert pas uniquement à des fins éducatives.

La réponse de anonym est excellente, mais il existe également une solution non macro en C ++ 98:

template <size_t N>
std::string RawString(const char (&ch)[N])
{
  return std::string(ch, N-1);  // Again, exclude trailing `null`
}

Avec cette fonction, RawString (/ * literal * /) produira la même chaîne que S (/ * literal * /) :

std::string my_string_t(RawString("a\0b"));
std::string my_string_m(S("a\0b"));
std::cout << "Using template: " << my_string_t << std::endl;
std::cout << "Using macro: " << my_string_m << std::endl;

De plus, il existe un problème avec la macro: l'expression n'est pas réellement std :: string telle qu'elle est écrite et ne peut donc pas être utilisée, par exemple. pour une simple affectation-initialisation:

std::string s = S("a\0b"); // ERROR!

... il serait donc préférable d'utiliser:

#define std::string(s, sizeof s - 1)

Évidemment, vous ne devez utiliser que l’une ou l’autre des solutions de votre projet et appelez-la comme bon vous semble.

Je sais que cette question a longtemps été posée. Mais pour ceux qui ont un problème similaire, le code suivant pourrait intéresser.

CComBSTR(20,"mystring1\0mystring2\0")

Presque toutes les implémentations de std :: strings sont terminées par un null, vous ne devriez donc probablement pas le faire. Notez que & a; a \ 0b " est en fait une longueur de quatre caractères en raison du terminateur null automatique (a, null, b, null). Si vous voulez vraiment faire cela et rompre le contrat de std :: string, vous pouvez faire:

std::string s("aab");
s.at(1) = '\0';

mais si vous le faites, tous vos amis se moqueront de vous, vous ne trouverez jamais le vrai bonheur.

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