Se voglio costruire uno std :: string con una riga come:
std::string my_string("a\0b");
Dove voglio avere tre caratteri nella stringa risultante (a, null, b), ne ottengo solo uno. Qual è la sintassi corretta?
Se voglio costruire uno std :: string con una riga come:
std::string my_string("a\0b");
Dove voglio avere tre caratteri nella stringa risultante (a, null, b), ne ottengo solo uno. Qual è la sintassi corretta?
Risposte:
siamo stati in grado di creare letterale 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";
}
Il problema è che il std::string
costruttore che assume a const char*
presuppone che l'input sia una stringa C. Le stringhe C vengono \0
terminate e quindi l'analisi si interrompe quando raggiunge il \0
carattere.
Per compensare ciò, è necessario utilizzare il costruttore che costruisce la stringa da un array di caratteri (non una C-String). Questo richiede due parametri: un puntatore all'array e una lunghezza:
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.
Nota: C ++ NONstd::string
è terminato (come suggerito in altri post). Tuttavia, è possibile estrarre un puntatore a un buffer interno che contiene una stringa C con il metodo . \0
c_str()
Controlla anche la risposta di Doug T di seguito sull'utilizzo di un file vector<char>
.
Controlla anche RiaD per una soluzione C ++ 14.
Se stai manipolando come faresti con una stringa in stile c (array di caratteri), considera l'utilizzo di
std::vector<char>
Hai più libertà di trattarlo come un array nello stesso modo in cui tratteresti una stringa c. Puoi usare copy () per copiare in una stringa:
std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());
e puoi usarlo in molti degli stessi posti in cui puoi usare le stringhe c
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
Naturalmente, tuttavia, soffri degli stessi problemi delle stringhe in c. Potresti dimenticare il tuo terminale null o scrivere oltre lo spazio allocato.
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;
Non ho idea del motivo per cui vorresti fare una cosa del genere, ma prova questo:
std::string my_string("a\0b", 3);
vector<unsigned char>
o unsigned char *
sono stati inventati.
std::string
per indicare che i dati devono essere considerati come testo normale, ma sto facendo un po 'di lavoro di hashing e voglio assicurarmi che tutto funzioni ancora con i caratteri nulli coinvolti. Sembra un uso valido di una stringa letterale con un carattere null incorporato.
\0
byte in una stringa UTF-8 può essere solo NUL. Un carattere codificato multibyte non conterrà mai - \0
né alcun altro carattere ASCII per quella materia.
Quali nuove funzionalità aggiungono al C ++ i valori letterali definiti dall'utente? presenta una risposta elegante: Definisci
std::string operator "" _s(const char* str, size_t n)
{
return std::string(str, n);
}
quindi puoi creare la tua stringa in questo modo:
std::string my_string("a\0b"_s);
o anche così:
auto my_string = "a\0b"_s;
C'è un modo "vecchio stile":
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
quindi puoi definire
std::string my_string(S("a\0b"));
Quanto segue funzionerà ...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
Dovrai stare attento con questo. Se sostituisci "b" con qualsiasi carattere numerico, creerai silenziosamente la stringa sbagliata utilizzando la maggior parte dei metodi. Vedere: Regole per i valori letterali stringa C ++ carattere di escape .
Ad esempio, ho lasciato cadere questo frammento dall'aspetto innocente nel mezzo di un programma
// 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";
Ecco cosa mi ha prodotto questo programma:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
Questa è stata la mia prima dichiarazione di stampa due volte, diversi caratteri non stampabili, seguiti da una nuova riga, seguita da qualcosa nella memoria interna, che ho appena sovrascritto (e poi stampato, dimostrando che è stato sovrascritto). Peggio ancora, anche la compilazione di questo con avvertimenti dettagliati e dettagliati di gcc non mi dava alcuna indicazione di qualcosa che non andava, e l'esecuzione del programma tramite valgrind non si lamentava di alcun modello di accesso alla memoria improprio. In altre parole, è completamente non rilevabile dagli strumenti moderni.
Puoi ottenere lo stesso problema con il molto più semplice std::string("0", 100);
, ma l'esempio sopra è un po 'più complicato e quindi più difficile da vedere cosa c'è che non va.
Fortunatamente, C ++ 11 ci fornisce una buona soluzione al problema utilizzando la sintassi dell'elenco di inizializzatori. Questo ti evita di dover specificare il numero di caratteri (che, come ho mostrato sopra, puoi fare in modo errato) ed evita di combinare numeri con escape. std::string str({'a', '\0', 'b'})
è sicuro per qualsiasi contenuto di stringa, a differenza delle versioni che accettano un array di char
e una dimensione.
In C ++ 14 ora puoi usare i letterali
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
auto s{"a\0b"s};
Meglio usare std :: vector <char> se questa domanda non è solo per scopi educativi.
la risposta di anonym è eccellente, ma c'è anche una soluzione non macro in C ++ 98:
template <size_t N>
std::string RawString(const char (&ch)[N])
{
return std::string(ch, N-1); // Again, exclude trailing `null`
}
Con questa funzione, RawString(/* literal */)
produrrà la stessa stringa di 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;
Inoltre, c'è un problema con la macro: l'espressione non è effettivamente std::string
come scritta, e quindi non può essere utilizzata, ad esempio, per una semplice inizializzazione dell'assegnazione:
std::string s = S("a\0b"); // ERROR!
... quindi potrebbe essere preferibile utilizzare:
#define std::string(s, sizeof s - 1)
Ovviamente dovresti usare solo l'una o l'altra soluzione nel tuo progetto e chiamarla come ritieni appropriato.
So che è da molto tempo che questa domanda viene posta. Ma per chiunque abbia un problema simile potrebbe essere interessato al seguente codice.
CComBSTR(20,"mystring1\0mystring2\0")
Quasi tutte le implementazioni di std :: strings hanno terminazione null, quindi probabilmente non dovresti farlo. Notare che "a \ 0b" è in realtà lungo quattro caratteri a causa del carattere di terminazione null automatico (a, null, b, null). Se vuoi davvero farlo e rompere il contratto di std :: string, puoi fare:
std::string s("aab");
s.at(1) = '\0';
ma se lo fai, tutti i tuoi amici rideranno di te, non troverai mai la vera felicità.