Domanda

Se voglio costruire uno std :: string con una linea 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?

È stato utile?

Soluzione

Dal C ++ 14

siamo stati in grado di creare std::string

letterali
#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";
}

Prima di C ++ 14

Il problema è il costruttore const char* che accetta un \0 presuppone che l'input sia una stringa C. Le stringhe C sono c_str() terminate e quindi l'analisi si interrompe quando raggiunge il vector<char> carattere.

Per compensare ciò, è necessario utilizzare il costruttore che costruisce la stringa da un array di caratteri (non una stringa C). 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 ++ <=> è NON <=> - terminato (come suggerito in altri post). Tuttavia, è possibile estrarre un puntatore a un buffer interno che contiene una stringa C con il metodo <=>.

Dai un'occhiata anche a Risposta di Doug T di seguito sull'uso di <=>.

Scopri anche RiaD per una soluzione C ++ 14.

Altri suggerimenti

Se stai manipolando come faresti con una stringa in stile c (array di caratteri), considera l'utilizzo di

std::vector<char>

Hai più libertà nel 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, si verificano gli stessi problemi delle stringhe C. Potresti dimenticare il tuo terminale null o scrivere oltre lo spazio assegnato.

Non ho idea perché vorresti fare una cosa del genere, ma prova questo:

std::string my_string("a\0b", 3);

Quali nuove funzionalità aggiungono letterali definiti dall'utente al C ++? 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 " vecchio stile " modo:

#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string

allora 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 usando la maggior parte dei metodi. Vedi: Le regole per i letterali di stringhe C ++ sfuggono al carattere .

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 ha prodotto questo programma per me:

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, seguiti da qualcosa nella memoria interna, che ho appena sovrascritto (e quindi stampato, dimostrando che è stato sovrascritto). Peggio ancora, anche compilando questo con gcc completo e dettagliato avvertimenti non mi ha dato alcuna indicazione di qualcosa che non va e l'esecuzione del programma attraverso valgrind non si è lamentata di eventuali schemi di accesso alla memoria impropri. In altre parole, è completamente non rilevabile dagli strumenti moderni.

Puoi ottenere questo stesso problema con il molto più semplice std::string("0", 100);, ma l'esempio sopra è un po 'più complicato e quindi più difficile vedere cosa c'è che non va.

Fortunatamente, C ++ 11 ci offre una buona soluzione al problema usando 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 i numeri di escape. std::string str({'a', '\0', 'b'}) è sicuro per qualsiasi contenuto di stringa, a differenza delle versioni che accettano una matrice 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

Meglio usare std :: vector < char > se questa domanda non è solo a scopo educativo.

la risposta di anonimo è 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 è in realtà un std::string come scritto e quindi non può essere utilizzata ad es. per una semplice inizializzazione dell'assegnazione:

std::string s = S("a\0b"); // ERROR!

... quindi potrebbe essere preferibile usare:

#define std::string(s, sizeof s - 1)

Ovviamente dovresti usare solo l'una o l'altra soluzione nel tuo progetto e chiamarla come preferisci.

So che è da molto tempo che questa domanda è stata 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 :: stringhe sono nulle, quindi probabilmente non dovresti farlo. Nota che & Quot; a \ 0b & Quot; è in realtà lungo quattro caratteri a causa del terminatore 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à.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top