Sie müssen damit vorsichtig sein. Wenn Sie 'b' durch ein numerisches Zeichen ersetzen, erstellen Sie stillschweigend die falsche Zeichenfolge. Siehe: stackoverflow.com/questions/10220401/…
David Stone
Antworten:
127
Seit C ++ 14
Wir konnten buchstäblich schaffen std::string
#include<iostream>#include<string>int main(){usingnamespace 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";}
Vor C ++ 14
Das Problem ist, dass der std::stringKonstruktor a const char*annimmt, dass die Eingabe eine C-Zeichenfolge ist. C-Strings werden \0beendet und das Parsen stoppt daher, wenn es das \0Zeichen erreicht.
Um dies zu kompensieren, müssen Sie den Konstruktor verwenden, der die Zeichenfolge aus einem char-Array (nicht einer C-Zeichenfolge) erstellt. Dies erfordert zwei Parameter - einen Zeiger auf das Array und eine Länge:
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.
Hinweis: C ++ std::stringwird NICHT beendet\0 (wie in anderen Beiträgen vorgeschlagen). Sie können jedoch mit der Methode einen Zeiger auf einen internen Puffer extrahieren, der einen C-String enthält c_str().
Lesen Sie auch die Antwort von Doug T über die Verwendung von a vector<char>.
Schauen Sie sich auch RiaD für eine C ++ 14-Lösung an.
Update: Ab C ++ 11 sind Zeichenfolgen nullterminiert. Davon abgesehen bleibt Lokis Beitrag gültig.
Matthewaveryusa
14
@mna: Sie sind in Bezug auf die Speicherung nullterminiert, jedoch nicht in dem Sinne, dass sie mit einer aussagekräftigen Nullterminierung (dh mit einer die Zeichenfolge längendefinierenden Semantik) nullterminiert sind , was die übliche Bedeutung des Begriffs ist.
Leichtigkeitsrennen im Orbit
Gut erklärt. Danke dir.
Joma
22
Wenn Sie Manipulationen wie mit einer Zeichenfolge im C-Stil (Array von Zeichen) durchführen, sollten Sie die Verwendung in Betracht ziehen
std::vector<char>
Sie haben mehr Freiheit, es wie ein Array zu behandeln, genauso wie Sie einen C-String behandeln würden. Sie können copy () verwenden, um in eine Zeichenfolge zu kopieren:
und Sie können es an vielen der gleichen Stellen verwenden, an denen Sie C-Strings verwenden können
printf("%s"&vec[0])
vec[10]='\0';
vec[11]='b';
Natürlich leiden Sie jedoch unter den gleichen Problemen wie C-Strings. Sie können Ihr Null-Terminal vergessen oder über den zugewiesenen Speicherplatz hinaus schreiben.
Wenn Sie versuchen, Bytes in Zeichenfolgen zu codieren (grpc-Bytes werden als Zeichenfolge gespeichert), verwenden Sie die in der Antwort angegebene Vektormethode. nicht die übliche Art (siehe unten), die NICHT die gesamte Zeichenfolge konstruiert 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
Ich habe keine Ahnung, warum Sie so etwas tun möchten, aber versuchen Sie Folgendes:
Was sind Ihre Bedenken dafür? Stellen Sie die Notwendigkeit in Frage, "a \ 0b" jemals zu speichern? oder die Verwendung eines std :: string für einen solchen Speicher in Frage stellen? Wenn letzteres, was schlagen Sie als Alternative vor?
Anthony Cramp
3
@Constantin, dann machen Sie etwas falsch, wenn Sie Binärdaten als Zeichenfolge speichern. Dafür wurden vector<unsigned char>oder unsigned char *wurden erfunden.
Mahmoud Al-Qudsi
2
Ich bin darauf gestoßen, als ich versucht habe, mehr über die Sicherheit von Zeichenfolgen zu erfahren. Ich wollte meinen Code testen, um sicherzustellen, dass er auch dann noch funktioniert, wenn er ein Nullzeichen einliest, während er aus einer Datei / einem Netzwerk liest, was er als Textdaten erwartet. Ich gebe std::stringan, dass die Daten als Klartext betrachtet werden sollen, aber ich mache einige Hashing-Arbeiten und möchte sicherstellen, dass alles immer noch mit Nullzeichen funktioniert. Dies scheint eine gültige Verwendung eines Zeichenfolgenliteral mit einem eingebetteten Nullzeichen zu sein.
David Stone
3
@ DuckMaestro Nein, das stimmt nicht. Ein \0Byte in einer UTF-8-Zeichenfolge kann nur NUL sein. Ein Multi-Byte-codiertes Zeichen enthält niemals - \0oder ein anderes ASCII-Zeichen.
John Kugelman
1
Ich bin darauf gestoßen, als ich versucht habe, einen Algorithmus in einem Testfall zu provozieren. Es gibt also triftige Gründe; wenn auch nur wenige.
Sie müssen Klammern in eckigen Klammern verwenden.
jk.
5
Sie müssen damit vorsichtig sein. Wenn Sie 'b' durch ein numerisches Zeichen ersetzen, erstellen Sie mit den meisten Methoden stillschweigend die falsche Zeichenfolge. Siehe: Regeln für C ++ - Zeichenfolgenliterale als Escapezeichen .
Zum Beispiel habe ich dieses unschuldig aussehende Snippet mitten in einem Programm abgelegt
// 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";
Folgendes hat dieses Programm für mich ausgegeben:
Das war meine erste Druckanweisung zweimal, mehrere nicht druckbare Zeichen, gefolgt von einem Zeilenumbruch, gefolgt von etwas im internen Speicher, das ich gerade überschrieben (und dann gedruckt habe, um zu zeigen, dass es überschrieben wurde). Das Schlimmste war, dass selbst das Kompilieren mit gründlichen und ausführlichen gcc-Warnungen keinen Hinweis darauf gab, dass etwas nicht stimmte, und das Ausführen des Programms über valgrind beschwerte sich nicht über falsche Speicherzugriffsmuster. Mit anderen Worten, es ist mit modernen Werkzeugen völlig nicht nachweisbar.
Sie können das gleiche Problem mit dem viel einfacheren bekommen std::string("0", 100);, aber das obige Beispiel ist etwas kniffliger und daher schwieriger zu erkennen, was falsch ist.
Glücklicherweise bietet C ++ 11 eine gute Lösung für das Problem mithilfe der Initialisierungslistensyntax. Dies erspart Ihnen die Angabe der Anzahl der Zeichen (was, wie oben gezeigt, falsch sein kann) und vermeidet das Kombinieren von Escape-Zahlen. std::string str({'a', '\0', 'b'})ist sicher für jeden String-Inhalt, im Gegensatz zu Versionen, die ein Array von charund eine Größe annehmen .
Als Teil meiner Vorbereitung auf diesen Beitrag habe ich gcc einen Fehlerbericht übermittelt, in der Hoffnung, dass eine Warnung hinzugefügt wird, um dies ein wenig sicherer zu machen: gcc.gnu.org/bugzilla/show_bug.cgi?id=54924
David Stone
4
In C ++ 14 können Sie jetzt Literale verwenden
usingnamespace std::literals::string_literals;
std::string s ="a\0b"s;
std::cout << s.size();// 3
Außerdem gibt es ein Problem mit dem Makro: Der Ausdruck ist nicht std::stringwie geschrieben und kann daher nicht verwendet werden, z. B. für eine einfache Zuweisungsinitialisierung:
std::string s = S("a\0b");// ERROR!
... daher ist es möglicherweise vorzuziehen, Folgendes zu verwenden:
#define std::string(s,sizeof s -1)
Natürlich sollten Sie in Ihrem Projekt nur die eine oder andere Lösung verwenden und sie so nennen, wie Sie es für angemessen halten.
Ich weiß, es ist lange her, dass diese Frage gestellt wurde. Aber für alle, die ein ähnliches Problem haben, könnte der folgende Code von Interesse sein.
Diese Antwort ist zu spezifisch für Microsoft-Plattformen und geht nicht auf die ursprüngliche Frage ein (die nach std :: string gestellt wurde).
Juni Rhodos
-8
Fast alle Implementierungen von std :: strings sind nullterminiert, daher sollten Sie dies wahrscheinlich nicht tun. Beachten Sie, dass "a \ 0b" aufgrund des automatischen Nullterminators (a, null, b, null) tatsächlich vier Zeichen lang ist. Wenn Sie dies wirklich tun und den Vertrag von std :: string brechen möchten, können Sie Folgendes tun:
std::string s("aab");
s.at(1)='\0';
Aber wenn Sie dies tun, werden alle Ihre Freunde Sie auslachen, Sie werden niemals wahres Glück finden.
Dies ist nicht erforderlich, aber in fast allen Implementierungen ist dies wahrscheinlich darauf zurückzuführen, dass der Accessor c_str () Ihnen das nullterminierte Äquivalent bereitstellen muss.
Jurney
2
Für effeciency ein Nullzeichen kann auf der Rückseite des Datenpuffers gehalten werden. Keine der Operationen (dh Methoden) für eine Zeichenfolge verwendet dieses Wissen oder wird von einer Zeichenfolge beeinflusst, die ein NULL-Zeichen enthält. Das NULL-Zeichen wird genauso wie jedes andere Zeichen bearbeitet.
Martin York
Aus diesem Grund ist es so lustig, dass der String std :: ist - sein Verhalten ist auf KEINER Plattform definiert.
Ich wünschte, user595447 wäre noch hier, damit ich sie fragen könnte, worüber in aller Welt sie zu reden glaubten.
Antworten:
Seit C ++ 14
Wir konnten buchstäblich schaffen
std::string
Vor C ++ 14
Das Problem ist, dass der
std::string
Konstruktor aconst char*
annimmt, dass die Eingabe eine C-Zeichenfolge ist. C-Strings werden\0
beendet und das Parsen stoppt daher, wenn es das\0
Zeichen erreicht.Um dies zu kompensieren, müssen Sie den Konstruktor verwenden, der die Zeichenfolge aus einem char-Array (nicht einer C-Zeichenfolge) erstellt. Dies erfordert zwei Parameter - einen Zeiger auf das Array und eine Länge:
Hinweis: C ++
std::string
wird NICHT beendet\0
(wie in anderen Beiträgen vorgeschlagen). Sie können jedoch mit der Methode einen Zeiger auf einen internen Puffer extrahieren, der einen C-String enthältc_str()
.Lesen Sie auch die Antwort von Doug T über die Verwendung von a
vector<char>
.Schauen Sie sich auch RiaD für eine C ++ 14-Lösung an.
quelle
Wenn Sie Manipulationen wie mit einer Zeichenfolge im C-Stil (Array von Zeichen) durchführen, sollten Sie die Verwendung in Betracht ziehen
Sie haben mehr Freiheit, es wie ein Array zu behandeln, genauso wie Sie einen C-String behandeln würden. Sie können copy () verwenden, um in eine Zeichenfolge zu kopieren:
und Sie können es an vielen der gleichen Stellen verwenden, an denen Sie C-Strings verwenden können
Natürlich leiden Sie jedoch unter den gleichen Problemen wie C-Strings. Sie können Ihr Null-Terminal vergessen oder über den zugewiesenen Speicherplatz hinaus schreiben.
quelle
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;
Ich habe keine Ahnung, warum Sie so etwas tun möchten, aber versuchen Sie Folgendes:
quelle
vector<unsigned char>
oderunsigned char *
wurden erfunden.std::string
an, dass die Daten als Klartext betrachtet werden sollen, aber ich mache einige Hashing-Arbeiten und möchte sicherstellen, dass alles immer noch mit Nullzeichen funktioniert. Dies scheint eine gültige Verwendung eines Zeichenfolgenliteral mit einem eingebetteten Nullzeichen zu sein.\0
Byte in einer UTF-8-Zeichenfolge kann nur NUL sein. Ein Multi-Byte-codiertes Zeichen enthält niemals -\0
oder ein anderes ASCII-Zeichen.Welche neuen Funktionen fügen benutzerdefinierte Literale C ++ hinzu? präsentiert eine elegante Antwort: Definieren
Dann können Sie Ihre Zeichenfolge folgendermaßen erstellen:
oder sogar so:
Es gibt einen "alten" Weg:
dann können Sie definieren
quelle
Folgendes wird funktionieren ...
quelle
Sie müssen damit vorsichtig sein. Wenn Sie 'b' durch ein numerisches Zeichen ersetzen, erstellen Sie mit den meisten Methoden stillschweigend die falsche Zeichenfolge. Siehe: Regeln für C ++ - Zeichenfolgenliterale als Escapezeichen .
Zum Beispiel habe ich dieses unschuldig aussehende Snippet mitten in einem Programm abgelegt
Folgendes hat dieses Programm für mich ausgegeben:
Das war meine erste Druckanweisung zweimal, mehrere nicht druckbare Zeichen, gefolgt von einem Zeilenumbruch, gefolgt von etwas im internen Speicher, das ich gerade überschrieben (und dann gedruckt habe, um zu zeigen, dass es überschrieben wurde). Das Schlimmste war, dass selbst das Kompilieren mit gründlichen und ausführlichen gcc-Warnungen keinen Hinweis darauf gab, dass etwas nicht stimmte, und das Ausführen des Programms über valgrind beschwerte sich nicht über falsche Speicherzugriffsmuster. Mit anderen Worten, es ist mit modernen Werkzeugen völlig nicht nachweisbar.
Sie können das gleiche Problem mit dem viel einfacheren bekommen
std::string("0", 100);
, aber das obige Beispiel ist etwas kniffliger und daher schwieriger zu erkennen, was falsch ist.Glücklicherweise bietet C ++ 11 eine gute Lösung für das Problem mithilfe der Initialisierungslistensyntax. Dies erspart Ihnen die Angabe der Anzahl der Zeichen (was, wie oben gezeigt, falsch sein kann) und vermeidet das Kombinieren von Escape-Zahlen.
std::string str({'a', '\0', 'b'})
ist sicher für jeden String-Inhalt, im Gegensatz zu Versionen, die ein Array vonchar
und eine Größe annehmen .quelle
In C ++ 14 können Sie jetzt Literale verwenden
quelle
auto s{"a\0b"s};
Verwenden Sie besser std :: vector <char>, wenn diese Frage nicht nur zu Bildungszwecken dient.
quelle
Die Antwort von anonym ist ausgezeichnet, aber es gibt auch eine Nicht-Makro-Lösung in C ++ 98:
Mit dieser Funktion
RawString(/* literal */)
wird dieselbe Zeichenfolge erzeugt wieS(/* literal */)
:Außerdem gibt es ein Problem mit dem Makro: Der Ausdruck ist nicht
std::string
wie geschrieben und kann daher nicht verwendet werden, z. B. für eine einfache Zuweisungsinitialisierung:... daher ist es möglicherweise vorzuziehen, Folgendes zu verwenden:
Natürlich sollten Sie in Ihrem Projekt nur die eine oder andere Lösung verwenden und sie so nennen, wie Sie es für angemessen halten.
quelle
Ich weiß, es ist lange her, dass diese Frage gestellt wurde. Aber für alle, die ein ähnliches Problem haben, könnte der folgende Code von Interesse sein.
quelle
Fast alle Implementierungen von std :: strings sind nullterminiert, daher sollten Sie dies wahrscheinlich nicht tun. Beachten Sie, dass "a \ 0b" aufgrund des automatischen Nullterminators (a, null, b, null) tatsächlich vier Zeichen lang ist. Wenn Sie dies wirklich tun und den Vertrag von std :: string brechen möchten, können Sie Folgendes tun:
Aber wenn Sie dies tun, werden alle Ihre Freunde Sie auslachen, Sie werden niemals wahres Glück finden.
quelle