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 reçois qu'un. Quelle est la syntaxe appropriée?
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 reçois qu'un. Quelle est la syntaxe appropriée?
Réponses:
nous avons pu créer littéralement 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";
}
Le problème est que le std::string
constructeur qui prend a const char*
suppose que l'entrée est une chaîne C. Les chaînes C sont \0
terminées et ainsi l'analyse s'arrête quand elle atteint le \0
caractère.
Pour compenser cela, vous devez utiliser le constructeur qui construit la chaîne à partir d'un tableau de caractères (pas une chaîne C). Cela prend deux paramètres - un pointeur vers 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 ++ std::string
n'est PAS \0
terminé (comme suggéré dans d'autres articles). Toutefois, vous pouvez extraire un pointeur vers un tampon interne qui contient une chaîne C avec la méthode c_str()
.
Consultez également la réponse de Doug T ci-dessous sur l'utilisation d'un vector<char>
.
Consultez également RiaD pour une solution C ++ 14.
Si vous effectuez 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 c. 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 de nombreux endroits où vous pouvez utiliser des chaînes de caractères
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
Naturellement, cependant, vous souffrez des mêmes problèmes que les c-strings. Vous pouvez oublier votre terminal nul ou écrire au-delà de l'espace alloué.
byte *bytes = new byte[dataSize]; std::memcpy(bytes, image.data, dataSize * sizeof(byte)); std::string test(reinterpret_cast<char *>(bytes)); std::cout << "Encoded String length " << test.length() << std::endl;
Je ne sais pas pourquoi vous voudriez faire une telle chose, mais essayez ceci:
std::string my_string("a\0b", 3);
vector<unsigned char>
ou unsigned char *
ont été inventés.
std::string
pour indiquer que les données doivent être considérées comme du texte brut, mais je fais un travail de hachage et je veux m'assurer que tout fonctionne toujours avec des caractères nuls impliqués. Cela semble être une utilisation valide d'un littéral de chaîne avec un caractère nul incorporé.
\0
octet dans une chaîne UTF-8 ne peut être que NUL. Un caractère codé sur plusieurs octets ne contiendra jamais - \0
ni aucun autre caractère ASCII d'ailleurs.
Quelles nouvelles fonctionnalités les littéraux définis par l'utilisateur ajoutent-ils à C ++? présente 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 une manière «à l'ancienne»:
#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 être prudent avec cela. Si vous remplacez «b» par un caractère numérique, vous créerez silencieusement la mauvaise chaîne en utilisant la plupart des méthodes. Voir: Règles pour le caractère d'échappement des littéraux de chaîne C ++ .
Par exemple, j'ai laissé tomber cet extrait de code 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 produit pour moi:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
C'était ma première déclaration imprimée deux fois, plusieurs caractères non imprimables, suivis d'un saut de ligne, suivi de quelque chose dans la mémoire interne, que j'ai juste écrasé (puis imprimé, montrant qu'il a été écrasé). Le pire de tout, même compiler cela avec des avertissements gcc détaillés et détaillés ne m'a donné aucune indication que quelque chose n'allait pas, et l'exécution du programme via valgrind ne se plaignait pas des modèles d'accès à la mémoire incorrects. En d'autres termes, il est complètement indétectable par les outils modernes.
Vous pouvez obtenir le même problème avec le plus simple std::string("0", 100);
, mais l'exemple ci-dessus est un peu plus délicat, et donc plus difficile à voir 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 ne pouvez pas faire correctement), et évite de combiner des nombres échappés. std::string str({'a', '\0', 'b'})
est sans danger pour tout contenu de chaîne, contrairement aux versions qui prennent 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
auto s{"a\0b"s};
Mieux vaut utiliser std :: vector <char> si cette question n'est pas uniquement à des fins éducatives.
La réponse d'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 y a un problème avec la macro: l'expression n'est pas réellement une std::string
telle qu'elle est écrite, et ne peut donc pas être utilisée par exemple pour une simple initialisation d'affectation:
std::string s = S("a\0b"); // ERROR!
... il peut donc être préférable d'utiliser:
#define std::string(s, sizeof s - 1)
Évidemment, vous ne devez utiliser que l'une ou l'autre solution dans votre projet et l'appeler comme vous le jugez approprié.
Je sais que cela fait longtemps que cette question est posée. Mais pour quiconque rencontre un problème similaire pourrait être intéressé par le code suivant.
CComBSTR(20,"mystring1\0mystring2\0")
Presque toutes les implémentations de std :: strings sont terminées par null, donc vous ne devriez probablement pas faire cela. Notez que "a \ 0b" comporte en fait quatre caractères en raison du terminateur nul 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.