Non sono in grado di capire le differenze tra std::string
e std::wstring
. So che wstring
supporta caratteri ampi come i caratteri Unicode. Ho le seguenti domande:
std::wstring
su std::string
? std::string
contenere l’intero set di caratteri ASCII, inclusi i caratteri speciali? std::wstring
supportato da tutti i popolari compilatori C ++? string
? wstring
? std::string
è una std::string
basic_string
su un char
e std::wstring
su un wchar_t
.
char
vs. wchar_t
char
dovrebbe contenere un carattere, di solito un carattere da 1 byte. wchar_t
dovrebbe contenere un carattere ampio e quindi le cose si complicano: su Linux, un wchar_t
è 4 byte, mentre su Windows, sono 2 byte
Il problema è che né char
né wchar_t
sono direttamente collegati all’unicode.
Prendiamo un sistema operativo Linux: il mio sistema Ubuntu è già sensibile all’unicode. Quando lavoro con una stringa di caratteri, è codificata in modo nativo in UTF-8 (cioè una stringa di caratteri Unicode). Il seguente codice:
#include #include int main(int argc, char* argv[]) { const char text[] = "olé" ; std::cout << "sizeof(char) : " << sizeof(char) << std::endl ; std::cout << "text : " << text << std::endl ; std::cout << "sizeof(text) : " << sizeof(text) << std::endl ; std::cout << "strlen(text) : " << strlen(text) << std::endl ; std::cout << "text(bytes) :" ; for(size_t i = 0, iMax = strlen(text); i < iMax; ++i) { std::cout << " " << static_cast( static_cast (text[i]) ); } std::cout << std::endl << std::endl ; // - - - const wchar_t wtext[] = L"olé" ; std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ; //std::cout << "wtext : " << wtext << std::endl ; <- error std::cout << "wtext : UNABLE TO CONVERT NATIVELY." << std::endl ; std::wcout << L"wtext : " << wtext << std::endl; std::cout << "sizeof(wtext) : " << sizeof(wtext) << std::endl ; std::cout << "wcslen(wtext) : " << wcslen(wtext) << std::endl ; std::cout << "wtext(bytes) :" ; for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i) { std::cout << " " << static_cast ( static_cast (wtext[i]) ); } std::cout << std::endl << std::endl ; return 0; }
emette il seguente testo:
sizeof(char) : 1 text : olé sizeof(text) : 5 strlen(text) : 4 text(bytes) : 111 108 195 169 sizeof(wchar_t) : 4 wtext : UNABLE TO CONVERT NATIVELY. wtext : ol sizeof(wtext) : 16 wcslen(wtext) : 3 wtext(bytes) : 111 108 233
Vedrete che il testo "olé" in char
è in realtà composto da quattro caratteri: 110, 108, 195 e 169 (senza contare lo zero finale). (Ti farò studiare il codice wchar_t
come esercizio)
Quindi, quando si lavora con un char su Linux, di solito si finisce per usare Unicode senza nemmeno saperlo. E come std :: string funziona con char, quindi std :: string è già pronto per Unicode.
Nota che std :: string, come l'API della stringa C, considererà la stringa "olé" con 4 caratteri, non tre. Quindi dovresti essere cauto quando troncare / giocare con caratteri unicode perché una combinazione di caratteri è proibita in UTF-8.
Su Windows, questo è un po 'diverso. Win32 doveva supportare molte applicazioni funzionanti con char
e su diversi set di caratteri / codepage prodotti in tutto il mondo, prima dell'avvento di Unicode.
Quindi la loro soluzione era interessante: se un'applicazione funziona con char
, le stringhe di caratteri vengono codificate / stampate / mostrate sulle etichette della GUI usando il set di caratteri / codepage locale sulla macchina. Ad esempio, "olé" sarebbe "olé" in un Windows localizzato in francese, ma sarebbe qualcosa di diverso su un Windows localizzato in cirillico ("ol" se si usa Windows-1251 ). Pertanto, le "app storiche" di solito funzionano sempre allo stesso modo.
Per le applicazioni basate su Unicode, Windows usa wchar_t
, che è largo 2 byte, ed è codificato in UTF-16 , che è codificato in Unicode su caratteri da 2 byte (o per lo meno, l'UCS-2 per lo più compatibile, che è quasi la stessa cosa IIRC).
Le applicazioni che usano char
sono dette "multibyte" (poiché ogni glifo è composto da uno o più char
), mentre le applicazioni che usano wchar_t
sono dette "widechar" (poiché ciascun glifo è composto da uno o due wchar_t
. Vedi API di conversione Win32 MultiByteToWideChar e WideCharToMultiByte per maggiori informazioni.
Quindi, se lavori su Windows, vuoi usare wchar_t
(a meno che non usi un framework che nasconde quello, come GTK + o QT ...). Il fatto è che dietro le quinte, Windows funziona con le stringhe wchar_t
, quindi anche le applicazioni storiche convertiranno le stringhe di char
in wchar_t
quando usano API come SetWindowText (funzione API di basso livello per impostare l'etichetta su una GUI Win32).
UTF-32 è 4 byte per caratteri, quindi non c'è molto da aggiungere, se solo un testo UTF-8 e un testo UTF-16 utilizzeranno sempre meno o la stessa quantità di memoria di un testo UTF-32 (e di solito meno ).
Se c'è un problema di memoria, allora dovresti sapere che rispetto alla maggior parte delle lingue occidentali, il testo UTF-8 userà meno memoria rispetto allo stesso UTF-16.
Tuttavia, per le altre lingue (cinese, giapponese, ecc.), La memoria utilizzata sarà uguale o più grande per UTF-8 che per UTF-16.
Tutto sumto, UTF-16 utilizzerà principalmente 2 byte per carattere (a meno che non abbiate a che fare con un qualche tipo di glifo del linguaggio esoterico (Klingon? Elvish?), Mentre UTF-8 passerà da 1 a 4 byte.
Vedi http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 per maggiori informazioni.
1. Quando dovrei usare std :: wstring su std :: string?
Su Linux? Quasi mai (§).
Su Windows? Quasi sempre (§).
Nel codice multipiattaforma? Dipende dal tuo toolkit ...
(§): a meno che non si usi un toolkit / framework che dice diversamente
2. Can std :: string contiene tutto il set di caratteri ASCII compresi i caratteri speciali?
Nota: una stringa std :: è adatta per contenere un buffer 'binario', dove non è std :: wstring!
Su Linux? Sì.
Su Windows? Sono disponibili solo caratteri speciali per le impostazioni internazionali correnti dell'utente Windows.
Modifica (Dopo un commento di Johann Gerell ): una stringa std :: string sarà sufficiente per gestire tutte le stringhe basate su char (ogni char è un numero compreso tra 0 e 255). Ma:
3. std: wstring è supportato da quasi tutti i popolari compilatori C ++?
Principalmente, ad eccezione dei compilatori basati su GCC portati su Windows
Funziona sul mio g ++ 4.3.2 (sotto Linux), e ho usato l'API Unicode su Win32 da Visual C ++ 6.
4. Che cos'è esattamente un personaggio ampio?
Su C / C ++, è un tipo di carattere scritto wchar_t
che è più grande del semplice tipo di carattere char
. Dovrebbe essere usato per mettere dentro i caratteri i cui indici (come glifi Unicode) sono più grandi di 255 (o 127, a seconda ...)
Raccomando di evitare std::wstring
su Windows o altrove, tranne quando richiesto dall’interfaccia, o ovunque vicino alle chiamate API di Windows e alle rispettive conversioni di codifica come zucchero sintattico.
La mia opinione è riassunta in http://utf8everywhere.org di cui sono un co-autore.
A meno che la tua applicazione non sia incentrata sulla chiamata dell’API, ad esempio principalmente l’interfaccia utente, il suggerimento è di memorizzare stringhe Unicode in std :: string e codificate in UTF-8, eseguendo la conversione vicino alle chiamate API. I benefici delineati nell’articolo superano l’apparente fastidio della conversione, specialmente in applicazioni complesse. Questo è doppiamente così per lo sviluppo di librerie e piattaforms multiple.
E ora, rispondendo alle tue domande:
Quindi, ogni lettore qui ora dovrebbe avere una chiara comprensione dei fatti, della situazione. In caso contrario, è necessario leggere la risposta eccezionalmente completa di paercebal [btw: thanks!].
La mia conclusione pragmatica è sorprendentemente semplice: tutta quella roba “codifica caratteri” C ++ (e STL) è sostanzialmente rotta e inutile. Dai la colpa a Microsoft o no, che comunque non ti aiuterà.
La mia soluzione, dopo un’indagine approfondita, molta frustrazione e le conseguenti esperienze sono le seguenti:
accetta, che devi essere responsabile da solo per la codifica e la conversione (e vedrai che gran parte di essa è piuttosto banale)
usa std :: string per qualsiasi stringa codificata UTF-8 (solo un typedef std::string UTF8String
)
accetta che un object UTF8String sia solo un contenitore stupido ma economico. Non accedere mai e / o manipolare mai i caratteri direttamente (nessuna ricerca, sostituzione e così via). Potresti, ma davvero, davvero, non vuoi perdere tempo a scrivere algoritmi di manipolazione del testo per stringhe multi-byte! Anche se altre persone hanno già fatto cose così stupide, non farlo! Lascia fare! (Beh, ci sono scenari in cui ha senso … basta usare la libreria ICU per quelli).
usa std :: wstring per stringhe codificate in UCS-2 ( typedef std::wstring UCS2String
) – questo è un compromesso e una concessione al pasticcio introdotto dall’API WIN32). UCS-2 è sufficiente per la maggior parte di noi (ne riparleremo più avanti …).
utilizzare le istanze UCS2String ogni volta che è richiesto un accesso carattere per carattere (leggi, manipola e così via). Qualsiasi elaborazione basata sui caratteri dovrebbe essere eseguita in una rappresentazione NON multibyte. È semplice, veloce, facile.
aggiungere due funzioni di utilità per convertire avanti e indietro tra UTF-8 e UCS-2:
UCS2String ConvertToUCS2( const UTF8String &str ); UTF8String ConvertToUTF8( const UCS2String &str );
Le conversioni sono semplici, Google dovrebbe aiutare qui …
Questo è tutto. Usa UTF8String ovunque la memoria sia preziosa e per tutti gli I / O UTF-8. Utilizzare UCS2String ovunque sia necessario analizzare e / o manipolare la stringa. È ansible convertire tra queste due rappresentazioni in qualsiasi momento.
Alternative e miglioramenti
le conversioni da & a codifiche di caratteri a singolo byte (es. ISO-8859-1) possono essere realizzate con l’ausilio di semplici tabelle di conversione, ad esempio const wchar_t tt_iso88951[256] = {0,1,2,...};
e codice appropriato per la conversione in & da UCS2.
se UCS-2 non è sufficiente, passare a UCS-4 ( typedef std::basic_string
)
ICU o altre librerie Unicode?
Per cose avanzate.
Quando vuoi inserire caratteri estesi nella tua stringa. wide
dipende dall’implementazione. Visual C ++ imposta automaticamente a 16 bit se ricordo correttamente, mentre GCC si imposta in base alla destinazione. È lungo 32 bit qui. Si noti che wchar_t (tipo di carattere esteso) non ha nulla a che fare con unicode. È semplicemente garantito che è ansible memorizzare tutti i membri del set di caratteri più grande supportato dall’implementazione dalle sue localizzazioni e almeno fino a quando char. È ansible memorizzare stringhe unicode bene in std::string
utilizzando anche la codifica utf-8
. Ma non capirà il significato dei punti di codice unicode. Quindi str.size()
non ti darà la quantità di caratteri logici nella tua stringa, ma semplicemente la quantità di elementi char o wchar_t memorizzati in quella stringa / wstring. Per questo motivo, i wrapper C ++ gtk / glib hanno sviluppato una class Glib::ustring
grado di gestire utf-8.
Se il tuo wchar_t è lungo 32 bit, puoi usare utf-32
come codifica unicode, e puoi memorizzare e gestire stringhe unicode usando una codifica fissa (utf-32 è una lunghezza fissa). Ciò significa che la funzione s.size()
del s.size()
restituirà la giusta quantità di elementi wchar_t e caratteri logici.
Uso spesso std :: string per mantenere i caratteri utf-8 senza alcun problema. Raccomando caldamente di farlo durante l’interfacciamento con le API che usano anche utf-8 come tipo di stringa nativa.
Per esempio, io uso utf-8 quando interfaccia il mio codice con l’interprete Tcl.
L’avvertenza principale è la lunghezza di std :: string, non è più il numero di caratteri nella stringa.
Le applicazioni che non soddisfano solo 256 caratteri diversi hanno le opzioni di utilizzare caratteri ampi (più di 8 bit) o una codifica a lunghezza variabile (una codifica multibyte in terminologia C ++) come UTF-8. I caratteri ampi generalmente richiedono più spazio di una codifica a lunghezza variabile, ma sono più veloci da elaborare. Le applicazioni multi-lingua che elaborano grandi quantità di testo in genere utilizzano caratteri ampi durante l’elaborazione del testo, ma convertirlo in UTF-8 quando lo si archivia su disco.
L’unica differenza tra una string
e un wstring
è il tipo di dati dei caratteri che memorizzano. Una stringa memorizza i char
i cui dimensioni sono garantite da almeno 8 bit, quindi è ansible utilizzare stringhe per l’elaborazione, ad esempio testo ASCII, ISO-8859-15 o UTF-8. Lo standard non dice nulla sul set di caratteri o sulla codifica.
Praticamente ogni compilatore usa un set di caratteri i cui primi 128 caratteri corrispondono ad ASCII. Questo è anche il caso con i compilatori che usano la codifica UTF-8. La cosa importante da sapere quando si usano stringhe in UTF-8 o in qualche altra codifica a lunghezza variabile, è che gli indici e le lunghezze sono misurati in byte, non in caratteri.
Il tipo di dati di un wstring è wchar_t
, la cui dimensione non è definita nello standard, tranne per il fatto che deve essere grande almeno quanto un char, solitamente 16 bit o 32 bit. il wstring può essere utilizzato per elaborare il testo nella codifica wide-character definita dall’implementazione. Poiché la codifica non è definita nello standard, non è semplice convertire tra stringhe e stringhe. Non si può nemmeno pensare che le wstrings abbiano una codifica a lunghezza fissa.
Se non hai bisogno di supporto multilingue, potresti usare solo le stringhe normali. D’altra parte, se stai scrivendo un’applicazione grafica, è spesso il caso che l’API supporti solo caratteri ampi. Quindi probabilmente si desidera utilizzare gli stessi caratteri ampi durante l’elaborazione del testo. Tieni presente che UTF-16 è una codifica a lunghezza variabile, ovvero che non puoi assumere length()
per restituire il numero di caratteri. Se l’API utilizza una codifica a lunghezza fissa, come UCS-2, l’elaborazione diventa facile. La conversione tra caratteri wide e UTF-8 è difficile da eseguire in un modo portabile, ma poi di nuovo, l’API dell’interfaccia utente probabilmente supporta la conversione.
1) Come menzionato da Greg, wstring è utile per l’internazionalizzazione, è allora che rilascerai il tuo prodotto in lingue diverse dall’inglese
4) Controlla questo per carattere ampio http://en.wikipedia.org/wiki/Wide_character
Una buona domanda! Penso che DATA ENCODING (a volte CHARSET coinvolto anche) è un meccanismo di espressione memoria per salvare i dati in file o trasferire dati via rete, quindi rispondo a questa domanda come:
1.Quando dovrei usare std :: wstring su std :: string?
Se la piattaforma di programmazione o la funzione API è a singolo byte e vogliamo elaborare o analizzare alcuni dati unicode, ad es. Letti dal file .REG di Windows o dal stream di 2 byte di rete, dovremmo dichiarare la variabile std :: wstring a easy elaborarli. es: wstring ws = L “中国 a” (memoria 6 ottetti: 0x4E2D 0x56FD 0x0061), possiamo usare ws [0] per ottenere carattere ‘中’ e ws [1] per ottenere carattere ‘国’ e ws [2] per prendi il carattere ‘a’, ecc.
2.Can std :: string tieni premuto l’intero set di caratteri ASCII, inclusi i caratteri speciali?
Sì. Ma nota: ASCII americano, significa che ogni ottetto 0x00 ~ 0xFF rappresenta un carattere, incluso il testo stampabile come “123abc & * _ &” e hai detto uno speciale, per lo più stampalo come un “.” evitare di confondere editori o terminali. E alcuni altri paesi estendono il proprio set di caratteri “ASCII”, ad esempio il cinese, usano 2 ottetti per rappresentare un personaggio.
3.I std: wstring è supportato da tutti i popolari compilatori C ++?
Forse, o soprattutto. Ho usato: VC ++ 6 e GCC 3.3, SÌ
4. Cos’è esattamente un “ampio carattere”?
il carattere ampio indica principalmente l’uso di 2 ottetti o 4 ottetti per contenere i caratteri di tutti i paesi. 2 ottetti UCS2 è un campione rappresentativo, e inoltre ad esempio inglese ‘a’, la sua memoria è 2 ottetto di 0x0061 (vs in ASCII ‘la memoria è 1 ottetto 0x61)
Ci sono alcune ottime risposte qui, ma penso che ci siano un paio di cose che posso aggiungere riguardo a Windows / Visual Studio. Questo è basato sulla mia esperienza con VS2015. Su Linux, in pratica, la risposta è usare lo std::string
codificato UTF-8 ovunque. Su Windows / VS diventa più complesso. Ecco perché Windows si aspetta che le stringhe memorizzate utilizzando i char
vengano codificati utilizzando la tabella codici locale. Questo è quasi sempre il set di caratteri ASCII seguito da altri 128 caratteri speciali a seconda della posizione. Lasciatemi solo precisare che questo non solo quando si utilizza l’API di Windows, ci sono altri tre punti principali in cui queste stringhe interagiscono con lo standard C ++. Questi sono letterali stringa, output a std::cout
usando <<
e passando un nome file a std::fstream
.
Sarò di fronte qui che sono un programmatore, non uno specialista di lingue. Apprezzo che USC2 e UTF-16 non siano gli stessi, ma per i miei scopi sono abbastanza vicini per essere intercambiabili e li uso come tali qui. In realtà non sono sicuro di quale Windows usi, ma generalmente non ho bisogno di saperlo. Ho dichiarato UCS2 in questa risposta, mi dispiace così tanto in anticipo se sconvolgo qualcuno con la mia ignoranza su questo argomento e sono felice di cambiarlo se ho delle cose sbagliate.
Se si immettono valori letterali stringa che contengono solo caratteri che possono essere rappresentati dalla codepage, VS li memorizza nel file con codifica di 1 byte per carattere in base alla codepage. Notare che se si modifica la codepage o si fornisce la propria origine a un altro sviluppatore che utilizza una diversa tabella codici, penso (ma non ho verificato) che il personaggio finirà per essere diverso. Se esegui il tuo codice su un computer utilizzando una diversa tabella codici, non sono sicuro che cambierà anche il personaggio.
If you enter any string literals that cannot be represented by your codepage then VS will ask you to save the file as Unicode. The file will then be encoded as UTF-8. This means that all Non ASCII characters (including those which are on your codepage) will be represented by 2 or more bytes. This means if you give your source to someone else the source will look the same. However, before passing the source to the compiler, VS converts the UTF-8 encoded text to code page encoded text and any characters missing from the code page are replaced with ?
.
The only way to guarantee correctly representing a Unicode string literal in VS is to precede the string literal with an L
making it a wide string literal. In this case VS will convert the UTF-8 encoded text from the file into UCS2. You then need to pass this string literal into a std::wstring
constructor or you need to convert it to utf-8 and put it in a std::string
. Or if you want you can use the Windows API functions to encode it using your code page to put it in a std::string
, but then you may as well have not used a wide string literal.
When outputting to the console using <<
you can only use std::string
, not std::wstring
and the text must be encoded using your locale codepage. If you have a std::wstring
then you must convert it using one of the Windows API functions and any characters not on your codepage get replaced by ?
(maybe you can change the character, I can't remember).
Windows OS uses UCS2/UTF-16 for its filenames so whatever your codepage, you can have files with any Unicode character. But this means that to access or create files with characters not on your codepage you must use std::wstring
. Non c'è altro modo. This is a Microsoft specific extension to std::fstream
so probably won't compile on other systems. If you use std::string then you can only utilise filenames that only include characters on your codepage.
If you are just working on Linux then you probably didn't get this far. Just use UTF-8 std::string
everywhere.
If you are just working on Windows just use UCS2 std::wstring
everywhere. Some purists may say use UTF8 then convert when needed, but why bother with the hassle.
If you are cross platform then it's a mess to be frank. If you try to use UTF-8 everywhere on Windows then you need to be really careful with your string literals and output to the console. You can easily corrupt your strings there. If you use std::wstring
everywhere on Linux then you may not have access to the wide version of std::fstream
, so you have to do the conversion, but there is no risk of corruption. So personally I think this is a better option. Many would disagree, but I'm not alone - it's the path taken by wxWidgets for example.
Another option could be to typedef unicodestring
as std::string
on Linux and std::wstring
on Windows, and have a macro called UNI() which prefixes L on Windows and nothing on Linux, then the code
#include #include #include #include #ifdef _WIN32 typedef std::wstring unicodestring; #define UNI(text) L ## text std::string formatForConsole(const unicodestring &str) { std::string result; //Call WideCharToMultiByte to do the conversion return result; } #else typedef std::string unicodestring; #define UNI(text) text std::string formatForConsole(const unicodestring &str) { return str; } #endif int main() { unicodestring fileName(UNI("fileName")); std::ofstream fout; fout.open(fileName); std::cout << formatForConsole(fileName) << std::endl; return 0; }
would be fine on either platform I think.
So To answer your questions
1) If you are programming for Windows, then all the time, if cross platform then maybe all the time, unless you want to deal with possible corruption issues on Windows or write some code with platform specific #ifdefs
to work around the differences, if just using Linux then never.
2)Yes. In addition on Linux you can use it for all Unicode too. On Windows you can only use it for all unicode if you choose to manually encode using UTF-8. But the Windows API and standard C++ classs will expect the std::string
to be encoded using the locale codepage. This includes all ASCII plus another 128 characters which change depending on the codepage your computer is setup to use.
3)I believe so, but if not then it is just a simple typedef of a 'std::basic_string' using wchar_t
instead of char
4)A wide character is a character type which is bigger than the 1 byte standard char
type. On Windows it is 2 bytes, on Linux it is 4 bytes.
When should you NOT use wide-characters?
When you’re writing code before the year 1990.
Obviously, I’m being flip, but really, it’s the 21st century now. 127 characters have long since ceased to be sufficient. Yes, you can use UTF8, but why bother with the headaches?