Wenn ich einen std :: string mit einer Zeile wie der folgenden erstellen möchte:
std::string my_string("a\0b");
Wo ich drei Zeichen in der resultierenden Zeichenfolge haben möchte (a, null, b), bekomme ich nur eines. Was ist die richtige Syntax?
Wenn ich einen std :: string mit einer Zeile wie der folgenden erstellen möchte:
std::string my_string("a\0b");
Wo ich drei Zeichen in der resultierenden Zeichenfolge haben möchte (a, null, b), bekomme ich nur eines. Was ist die richtige Syntax?
Antworten:
Wir konnten buchstäblich schaffen 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";
}
Das Problem ist, dass der std::string
Konstruktor a const 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:
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::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ält c_str()
.
Lesen Sie auch die Antwort von Doug T über die Verwendung von a vector<char>
.
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:
std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());
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.
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:
std::string my_string("a\0b", 3);
vector<unsigned char>
oder unsigned 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
std::string operator "" _s(const char* str, size_t n)
{
return std::string(str, n);
}
Dann können Sie Ihre Zeichenfolge folgendermaßen erstellen:
std::string my_string("a\0b"_s);
oder sogar so:
auto my_string = "a\0b"_s;
Es gibt einen "alten" Weg:
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
dann können Sie definieren
std::string my_string(S("a\0b"));
Folgendes wird funktionieren ...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
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:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
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 char
und eine Größe annehmen .
In C ++ 14 können Sie jetzt Literale verwenden
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
auto s{"a\0b"s};
Verwenden Sie besser std :: vector <char>, wenn diese Frage nicht nur zu Bildungszwecken dient.
Die Antwort von anonym ist ausgezeichnet, aber es gibt auch eine Nicht-Makro-Lösung in C ++ 98:
template <size_t N>
std::string RawString(const char (&ch)[N])
{
return std::string(ch, N-1); // Again, exclude trailing `null`
}
Mit dieser Funktion RawString(/* literal */)
wird dieselbe Zeichenfolge erzeugt wie 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;
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:
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.
CComBSTR(20,"mystring1\0mystring2\0")
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.