Vous devrez être prudent avec cela. Si vous remplacez «b» par un caractère numérique, vous créerez silencieusement la mauvaise chaîne. Voir: stackoverflow.com/questions/10220401/…
David Stone
Réponses:
129
Depuis C ++ 14
nous avons pu créer littéralement std::string
#include<iostream>#include<string>intmain(){
usingnamespacestd::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 que le std::stringconstructeur qui prend a const char*suppose que l'entrée est une chaîne C. Les chaînes C sont \0terminées et ainsi l'analyse s'arrête quand elle atteint le \0caractè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::stringx("pq\0rs"); // Two characters because input assumed to be C-Stringstd::stringx("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.
Remarque: C ++ std::stringn'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.
mise à jour: à partir de C ++ 11, les chaînes se terminent par un NULL. Cela étant dit, le message de Loki reste valable.
matthewaveryusa
14
@mna: Ils sont terminés par un nul en termes de stockage, mais pas dans le sens où ils sont terminés par un nul avec une terminaison nulle significative (c'est-à-dire avec une sémantique définissant la longueur de chaîne), qui est le sens habituel du terme.
Courses de légèreté en orbite
Bien expliqué. Merci.
Joma
22
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:
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é.
Si vous dites essayer d'encoder des octets en chaîne (les octets grpc sont stockés sous forme de chaîne), utilisez la méthode vectorielle comme spécifié dans la réponse; pas la manière habituelle (voir ci-dessous) qui ne construira PAS la chaîne entière 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;
Alex Punnen
13
Je ne sais pas pourquoi vous voudriez faire une telle chose, mais essayez ceci:
Quelles sont vos préoccupations à ce sujet? Vous vous interrogez sur la nécessité de stocker "a \ 0b"? ou remettre en question l'utilisation d'une chaîne std :: string pour un tel stockage? Dans ce dernier cas, que proposez-vous comme alternative?
Anthony Cramp
3
@Constantin alors vous faites quelque chose de mal si vous stockez des données binaires sous forme de chaîne. C'est ce pour quoi vector<unsigned char>ou unsigned char *ont été inventés.
Mahmoud Al-Qudsi, le
2
Je suis tombé sur cela en essayant d'en savoir plus sur la sécurité des chaînes. Je voulais tester mon code pour m'assurer qu'il fonctionne toujours même s'il lit un caractère nul en lisant à partir d'un fichier / réseau ce qu'il s'attend à être des données textuelles. J'utilise std::stringpour 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é.
David Stone
3
@DuckMaestro Non, ce n'est pas vrai. Un \0octet dans une chaîne UTF-8 ne peut être que NUL. Un caractère codé sur plusieurs octets ne contiendra jamais - \0ni aucun autre caractère ASCII d'ailleurs.
John Kugelman
1
Je suis tombé sur cela en essayant de provoquer un algorithme dans un cas de test. Il y a donc des raisons valables; bien que peu nombreux.
Par exemple, j'ai laissé tomber cet extrait de code innocent au milieu d'un programme
// Create '\0' followed by '0' 40 times ;)std::stringstr("\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";
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 charet une taille.
Dans le cadre de ma préparation pour ce post, j'ai soumis un rapport de bogue à gcc dans l'espoir qu'ils ajouteront un avertissement pour rendre cela un peu plus sûr: gcc.gnu.org/bugzilla/show_bug.cgi?id=54924
David Stone
4
En C ++ 14, vous pouvez maintenant utiliser des littéraux
usingnamespacestd::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
De plus, il y a un problème avec la macro: l'expression n'est pas réellement une std::stringtelle 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.
Cette réponse est trop spécifique aux plates-formes Microsoft et ne répond pas à la question d'origine (qui posait sur std :: string).
Juin Rhodes
-8
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::strings("aab");
s.at(1) = '\0';
mais si vous le faites, tous vos amis se moqueront de vous, vous ne trouverez jamais le vrai bonheur.
std :: string n'est PAS nécessaire pour être terminé par NULL.
Martin York
2
Ce n'est pas obligatoire, mais dans presque toutes les implémentations, c'est probablement à cause de la nécessité pour l'accesseur c_str () de vous fournir l'équivalent terminé par null.
Jurney
2
Pour plus d'efficacité, un caractère nul peut être conservé à l'arrière du tampon de données. Mais aucune des opérations (c'est-à-dire des méthodes) sur une chaîne n'utilise cette connaissance ou n'est affectée par une chaîne contenant un caractère NULL. Le caractère NULL sera manipulé exactement de la même manière que tout autre caractère.
Martin York
C'est pourquoi il est si drôle que string soit std :: - son comportement n'est défini sur AUCUNE plateforme.
Je souhaite que user595447 soit toujours là pour que je puisse leur demander de quoi diable ils pensaient parler.
Réponses:
Depuis C ++ 14
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"; }
Avant C ++ 14
Le problème est que le
std::string
constructeur qui prend aconst 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éthodec_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.
la source
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é.
la source
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);
la source
vector<unsigned char>
ouunsigned 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"));
la source
Ce qui suit fonctionnera ...
std::string s; s.push_back('a'); s.push_back('\0'); s.push_back('b');
la source
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 dechar
et une taille.la source
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
la source
auto s{"a\0b"s};
Mieux vaut utiliser std :: vector <char> si cette question n'est pas uniquement à des fins éducatives.
la source
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 queS(/* 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é.
la source
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")
la source
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.
la source