C ++ succintamente archi

introduzione

Le stringhe sono una di quelle cose fastidiose in C e C ++. All'inizio dei linguaggi, le stringhe erano tutte matrici di caratteri, in genere ASCII a 7 bit (anche se forse EBCDIC su mainframe IBM su cui è stato eseguito il porting di C). Poi arrivò un casino di soluzioni alternative specifiche del sistema operativo, come le code page, per consentire le lingue con caratteri che non erano nell'alfabeto inglese. Dopo un periodo di caos, arrivò Unicode. Quindi Unicode. E poi ancora Unicode. E qualche altro Unicode qua e là, che è la radice del problema oggi.

Unicode è, in sostanza, due cose. È una serie definita di punti di codice in cui esiste una mappatura uno-a-uno di un particolare punto di codice a un valore particolare, alcuni sono grafici, altri controllano e manipolano la formattazione o forniscono altre informazioni richieste. Tutti coloro che utilizzano Unicode sono d'accordo su tutti questi, inclusi i punti di codice di uso privato, che sono tutti d'accordo per le applicazioni conformi a Unicode. Fin qui tutto bene.

Poi ci sono gli schemi di codifica da cui provengono le divisioni. Ci sono 1,114,112 punti codice in Unicode. Come li rappresenti? La risposta era gli schemi di codifica. UTF-16 è stato il primo. Successivamente è stato seguito da UTF-8 e UTF-32. Ci sono anche problemi di endianness con alcuni di questi.

Altri formati andavano e venivano, alcuni dei quali non erano mai nemmeno parte di Unicode.

Finalmente Windows ha adottato UTF-16 come .NET e Java. Molti sistemi GNU / Linux e altri sistemi simili a UNIX hanno adottato UTF-8. Alcuni sistemi simili a UNIX usano UTF-32. Alcuni potrebbero usare UTF-16. Il web usa UTF-8 per la maggior parte, a causa del fatto che il design intenzionale di quella codifica è per lo più retrocompatibile con ASCII. Finché stai lavorando su un sistema, tutto va bene. Quando si tenta di diventare multipiattaforma, le cose possono diventare più confuse.

stringhe

char * stringhe

Le stringhe char * (puntatori agli array di caratteri) inizialmente indicavano stringhe ASCII. Ora a volte significano ASCII, ma più frequentemente significano UTF-8. Questo è particolarmente vero nel mondo UNIX.

In generale, quando si programma Windows, è necessario assumere che una stringa char * sia una stringa ASCII o una stringa code-page. Le pagine di codice usano il bit in più rimasto da ASCII a 7 bit per aggiungere altri 128 caratteri, creando così un sacco di testo localizzato che si adatta ancora all'interno di un byte per carattere.

wchar_t * stringhe

wchar_t * stringhe (puntatori agli array di wchar_t, chiamato anche caratteri larghi) usa un set di caratteri diverso, dipendente dall'implementazione. Su Windows, questo significa un valore a 16 bit, che viene utilizzato per UTF-16. Dovresti sempre lavorare con wchar_t come tipo di carattere nativo per Windows, a meno che tu non debba supportare versioni del sistema operativo realmente vecchie (ad es. La vecchia serie Windows 9X).

Quando si scrive una costante di stringa di caratteri estesa nel codice, si precompongono le virgolette di apertura con una L. Ad esempio: const wchar_t * s = L "Hello World";. Se hai solo bisogno di un singolo carattere, usi di nuovo la L, ma con le virgolette singole: wchar_t ch = L'A ';.

std :: string e std :: wstring stringhe

Il std :: string e std :: wstring le classi si trovano nel file di intestazione. Come puoi immaginare, std :: string corrisponde a char * mentre std :: wstring corrisponde a wchar_t *.

Queste classi forniscono un modo conveniente per memorizzare stringhe di lunghezza variabile e devono essere utilizzate per le variabili dei membri della classe al posto dei loro puntatori raw corrispondenti (char * e wchar_t *). Dovresti usare solo i puntatori raw per passare le stringhe come argomenti, e solo se la stringa verrà usata così com'è o copiata localmente in uno di questi tipi di stringhe.

In entrambi i casi, la funzione dovrebbe assumere il puntatore a stringa come puntatore a const (ad es., const wchar_t * someStr). Dopotutto, i puntatori non comportano la stessa spesa di costruzione e distruzione che std :: string e std :: wstring fanno. L'utilizzo di un puntatore su const garantisce che la funzione non modifichi accidentalmente i dati o cerchi di liberare la memoria puntata.

Per ottenere un puntatore a const per i contenuti di uno di questi, chiama la sua funzione membro c_str. Si noti che il puntatore restituito punta a const poiché i dati non devono essere modificati, né dovrebbe essere richiamato sul puntatore. La memoria è ancora di proprietà e gestita dall'istanza std :: string o std :: wstring sottostante. Ciò significa anche che se l'istanza sottostante viene distrutta, il puntatore che c_str ti dà diventa non valido, motivo per cui, se hai bisogno dei dati della stringa oltre l'ambito della funzione a cui viene passato, devi sempre memorizzare i dati della stringa in uno di questi tipi invece di memorizzare direttamente il puntatore.

Per aggiungere del testo, utilizzare la funzione di membro dell'appendice.

Per vedere se una sequenza particolare di caratteri si verifica in una stringa, utilizzare la funzione di membro di ricerca o una delle sue varianti più specifiche, ad esempio find_first_of. Se la sequenza non è nella stringa, il valore restituito sarà uguale std :: ONP. Altrimenti, sarà l'indice del punto di partenza rilevante per la sequenza.

Per ottenere una sottostringa, utilizzare la funzione membro substr, passandogli l'indice basato su zero iniziale e il numero di elementi (ovvero il numero di caratteri char o wchar_t) da copiare. Restituirà uno std :: string o un std :: wstring senza consentire di sovrasfruttare un buffer passando un conteggio inesatto o un indice iniziale errato.

Esistono altri metodi utili, tutti documentati come parte della classe basic_string, che è una classe template che std :: string e std: wstring sono specializzazioni predefinite di.

std :: wstringstream stringhe

Il std :: wstringstream classe (c'è un std :: stringstream pure) è simile alla classe .NET StringBuilder. È utilizzabile quasi allo stesso modo di qualsiasi altro flusso di libreria standard C ++. Trovo questo tipo molto utile per la costruzione di una stringa all'interno di una funzione membro che verrà quindi memorizzata in a std :: wstring membro della classe.

Per un esempio del suo utilizzo, vedere il Condimenti :: GetString funzione membro nel file ConstructorsSample \ Toppings.h. Ecco il suo codice, proprio come un ripasso:

 const wchar_t * GetString (void) if (m_toppings == None) m_toppingsString = L "None"; return m_toppingsString.c_str ();  bool addSpace = false; std :: wstringstream wstrstream; if (m_toppings & HotFudge) if (addSpace) wstrstream << L" ";  wstrstream << L"Hot Fudge"; addSpace = true;  if (m_toppings & RaspberrySyrup)  if (addSpace)  wstrstream << L" ";  wstrstream << L"Raspberry Syrup"; addSpace = true;  if (m_toppings & CrushedWalnuts)  if (addSpace)  wstrstream << L" ";  wstrstream << L"Crushed Walnuts"; addSpace = true;  if (m_toppings & WhippedCream)  if (addSpace)  wstrstream << L" ";  wstrstream << L"Whipped Cream"; addSpace = true;  if (m_toppings & Cherry)  if (addSpace)  wstrstream << L" ";  wstrstream << L"Cherry"; addSpace = true;  m_toppingsString = std::wstring(wstrstream.str()); return m_toppingsString.c_str(); 

Conclusione

Come ho accennato nell'introduzione, la storia delle stringhe non è carina, ma spero che questo articolo ti abbia fornito una comprensione corretta delle stringhe in C ++. La prossima puntata di questa serie copre idiomi comuni in C++.

Questa lezione rappresenta un capitolo di C ++, un eBook gratuito del team di Syncfusion.