std :: wstring VS std :: string


742

Non sono in grado di comprendere le differenze tra std::stringe std::wstring. So che wstringsupporta caratteri ampi come i caratteri Unicode. Ho le seguenti domande:

  1. Quando devo utilizzare std::wstringsopra std::string?
  2. Può std::stringcontenere l'intero set di caratteri ASCII, inclusi i caratteri speciali?
  3. È std::wstringsupportato da tutti i compilatori C ++ più popolari?
  4. Che cos'è esattamente un " personaggio ampio "?

10
Il set di caratteri ASCII non ha molti caratteri "speciali", il più esotico è probabilmente `(backquote). std :: string può contenere circa lo 0,025% di tutti i caratteri Unicode (di solito, carattere a 8 bit)
MSalter

3
Buone informazioni su caratteri di grandi dimensioni e che tipo da utilizzare sono disponibili qui: programmers.stackexchange.com/questions/102205/…
Yariv

14
Bene, e dal momento che siamo nel 2012, è stato scritto utf8everywhere.org . Risponde praticamente a tutte le domande sui diritti e sui torti con C ++ / Windows.
Pavel Radzivilovsky,

42
@MSalters: std :: string può contenere il 100% di tutti i caratteri Unicode, anche se CHAR_BIT è 8. Dipende dalla codifica di std :: string, che può essere UTF-8 a livello di sistema (come quasi ovunque tranne Windows ) o a livello di applicazione. La codifica nativa stretta non supporta Unicode? Nessun problema, basta non usarlo, usa invece UTF-8.
Yakov Galka,

8
Ottima lettura su questo argomento: utf8everywhere.org
Timothy Shields,

Risposte:


993

string? wstring?

std::stringè un basic_stringmodello su a chare std::wstringsu a wchar_t.

char vs. wchar_t

chardovrebbe contenere un personaggio, di solito un personaggio a 8 bit.
wchar_tdovrebbe contenere un carattere ampio, quindi le cose si complicano:
su Linux, a wchar_tè di 4 byte, mentre su Windows è di 2 byte.

Che dire di Unicode , allora?

Il problema è che né charwchar_tè direttamente collegato all'unicode.

Su Linux?

Prendiamo un sistema operativo Linux: il mio sistema Ubuntu è già a conoscenza dell'unicode. Quando lavoro con una stringa di caratteri, viene codificato nativamente in UTF-8 (ovvero stringa di caratteri Unicode). Il seguente codice:

#include <cstring>
#include <iostream>

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(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(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(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

genera il seguente testo:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

Vedrai che il testo "olé" in charè davvero costruito da quattro caratteri: 110, 108, 195 e 169 (senza contare lo zero finale). (Ti lascerò studiare il wchar_tcodice come esercizio)

Quindi, quando si lavora con un charsu Linux, di solito si dovrebbe finire per usare Unicode senza nemmeno saperlo. E come std::stringfunziona char, std::stringè già pronto per Unicode.

Si noti che std::string, come l'API della stringa C, la stringa "olé" avrà 4 caratteri, non tre. Quindi dovresti essere cauto quando tronchi / giochi con caratteri unicode perché una combinazione di caratteri è proibita in UTF-8.

Su Windows?

Su Windows, questo è un po 'diverso. Win32 ha dovuto supportare molte applicazioni lavorando con chare su diversi set di caratteri / codici prodotto in tutto il mondo, prima dell'avvento di Unicode.

Quindi la loro soluzione è stata interessante: se un'applicazione funziona char, le stringhe di caratteri vengono codificate / stampate / mostrate sulle etichette della GUI usando il set di caratteri locale / tabella codici sulla macchina. Ad esempio, "olé" sarebbe "olé" in una Windows localizzata in francese, ma sarebbe qualcosa di diverso in una Windows localizzata in cirillico ("olé" se si utilizza Windows-1251 ). Pertanto, le "app storiche" di solito funzionano ancora allo stesso modo.

Per le applicazioni basate su Unicode, utilizza Windows wchar_t, che è largo 2 byte, ed è codificato in UTF-16 , che è codificato Unicode su caratteri a 2 byte (o almeno, l'UCS-2 per lo più compatibile, che è quasi il stessa cosa IIRC).

Applicazioni che utilizzano char sono dette "multibyte" (perché ogni glifo è composto da uno o più chars), mentre le applicazioni che usano wchar_tsono dette "widechar" (perché ogni glifo è composto da uno o due wchar_t. Vedere API di conversione MultiByteToWideChar e WideCharToMultiByte Win32 per maggiori informazioni.

Quindi, se lavori su Windows, vuoi davvero usarlo wchar_t(a meno che tu non usi un framework che lo nasconde, come GTK + o QT ...). Il fatto è che dietro le quinte, Windows funziona con le wchar_tstringhe, quindi anche le applicazioni storiche avranno le loro charstringhe convertite wchar_tquando si usano API come SetWindowText()(funzione API di basso livello per impostare l'etichetta su una GUI Win32).

Problemi di memoria?

UTF-32 è di 4 byte per carattere, quindi non c'è molto da aggiungere, se solo che 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 per la maggior parte delle lingue occidentali, il testo UTF-8 utilizzerà meno memoria rispetto allo stesso UTF-16.

Tuttavia, per altre lingue (cinese, giapponese, ecc.), La memoria utilizzata sarà la stessa o leggermente più grande per UTF-8 rispetto a UTF-16.

Tutto sommato, UTF-16 utilizzerà principalmente 2 e occasionalmente 4 byte per carattere (a meno che non si tratti di una sorta di glifi del linguaggio esoterico (Klingon? Elfico?), Mentre UTF-8 spenderà da 1 a 4 byte.

Vedi http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 per maggiori informazioni.

Conclusione

  1. Quando dovrei usare std :: wstring su std :: string?

    Su Linux? Quasi mai (§).
    Su Windows? Quasi sempre (§).
    Sul codice multipiattaforma? Dipende dal tuo toolkit ...

    (§): a meno che non si usi un toolkit / framework che dice diversamente

  2. Può std::stringcontenere tutti i set di caratteri ASCII inclusi i caratteri speciali?

    Avviso: A std::stringè adatto per contenere un buffer "binario", dove astd::wstring non lo è!

    Su Linux? Sì.
    Su Windows? Sono disponibili solo caratteri speciali per le impostazioni internazionali correnti dell'utente di Windows.

    Modifica (dopo un commento di Johann Gerell ):
    a std::stringsarà sufficiente per gestire tutte le charstringhe basate su base (ciascuna charessendo un numero compreso tra 0 e 255). Ma:

    1. Si suppone che ASCII vada da 0 a 127. charS più alti NON sono ASCII.
    2. uno charda 0 a 127 si terrà correttamente
    3. a charda 128 a 255 avrà un significato a seconda della codifica (unicode, non unicode, ecc.), ma sarà in grado di contenere tutti i glifi Unicode purché siano codificati in UTF-8.
  3. È std::wstringsupportato da quasi tutti i compilatori C ++ più popolari?

    Principalmente, ad eccezione dei compilatori basati su GCC che vengono 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_tche è più grande del semplice chartipo di carattere. Dovrebbe essere usato per mettere dentro caratteri i cui indici (come glifi Unicode) sono maggiori di 255 (o 127, a seconda ...).


4
@gnud: forse wchar_t doveva essere sufficiente per gestire tutti i caratteri UCS-2 (la maggior parte dei caratteri UTF-16) prima dell'avvento di UTF-16 ... O forse Microsoft aveva altre priorità rispetto a POSIX, come dare un facile accesso a Unicode senza modificare l'uso codificato di char su Win32.
Paercebal,

4
@Sorin Sbarnea: UTF-8 potrebbe richiedere 1-6 byte, ma a quanto pare lo standard lo limita a 1-4. Vedi en.wikipedia.org/wiki/UTF8#Description per ulteriori informazioni.
paercebal,

8
Mentre questo esempio produce risultati diversi su Linux e Windows, il programma C ++ contiene un comportamento definito dall'implementazione se olècodificato come UTF-8 o meno. Più ulteriormente più, la ragione per cui non si può nativamente lo streaming wchar_t *a std::coutè perché i tipi sono incompatibili con un conseguente programma di mal formata e non ha nulla a che fare con l'uso di codifiche. Vale la pena sottolineare che se usi std::stringo std::wstringdipende dalle tue preferenze di codifica piuttosto che dalla piattaforma, soprattutto se vuoi che il tuo codice sia portatile.
John Leidegren,

14
Windows in realtà utilizza UTF-16 e lo è da un po 'di tempo, le versioni precedenti di Windows utilizzavano UCS-2, ma non è più così. Il mio unico problema qui è la conclusione che std::wstringdovrebbe essere utilizzata su Windows perché si adatta meglio all'API Windows Unicode che ritengo fallace. Se la tua unica preoccupazione era chiamare l'API Windows Unicode e non eseguire il marshalling delle stringhe, sicuramente non lo compro come caso generale.
John Leidegren,

15
@ John Leidegren:: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureAllora, siamo d'accordo. Sto codificando in C ++, non JavaScript. Evitare il marshalling inutile o qualsiasi altra elaborazione potenzialmente costosa in fase di esecuzione quando può essere eseguita in fase di compilazione è al centro di quella lingua. La codifica su WinAPI e l'utilizzo std::stringè solo uno spreco ingiustificato di risorse di runtime. Lo trovi fallace, ed è OK, in quanto è il tuo punto di vista. La mia è che non scriverò codice con pessimizzazione su Windows solo perché sembra migliore dal lato Linux.
paercebal,

71

Consiglio 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 coautore.

A meno che l'applicazione non sia incentrata sulle API, ad esempio principalmente sull'interfaccia utente, il suggerimento è quello di memorizzare le stringhe Unicode in std :: string e codificate in UTF-8, eseguendo la conversione vicino alle chiamate API. I vantaggi descritti nell'articolo superano l'apparente fastidio della conversione, specialmente in applicazioni complesse. Questo è doppiamente lo sviluppo multipiattaforma e librerie.

E ora, rispondendo alle tue domande:

  1. Alcuni motivi deboli. Esiste per ragioni storiche, dove si riteneva che il widechar fosse il modo corretto di supportare Unicode. Ora è usato per interfacciare API che preferiscono le stringhe UTF-16. Li uso solo nelle immediate vicinanze di tali chiamate API.
  2. Questo non ha nulla a che fare con std :: string. Può contenere qualsiasi codifica inserita. L'unica domanda è come si trattano i suoi contenuti. La mia raccomandazione è UTF-8, quindi sarà in grado di contenere correttamente tutti i caratteri Unicode. È una pratica comune su Linux, ma penso che anche i programmi Windows dovrebbero farlo.
  3. No.
  4. Il personaggio ampio è un nome confuso. All'inizio di Unicode, si credeva che un personaggio potesse essere codificato in due byte, da cui il nome. Oggi rappresenta "qualsiasi parte del personaggio che è lunga due byte". UTF-16 è visto come una sequenza di tali coppie di byte (aka caratteri Wide). Un personaggio in UTF-16 accetta una o due coppie.

37

Quindi, ogni lettore qui ora dovrebbe avere una chiara comprensione dei fatti, della situazione. In caso contrario, devi leggere la risposta straordinariamente completa di paercebal [a proposito: grazie!].

La mia conclusione pragmatica è incredibilmente semplice: tutta quella roba di "codifica dei caratteri" C ++ (e STL) è sostanzialmente rotta e inutile. Dai la colpa a Microsoft o no, questo non aiuterà comunque.

La mia soluzione, dopo un'indagine approfondita, molta frustrazione e le conseguenti esperienze è la seguente:

  1. accetta, che devi essere responsabile da solo per le cose di codifica e conversione (e vedrai che gran parte di esso è piuttosto banale)

  2. usa std :: string per qualsiasi stringa codificata UTF-8 (solo a typedef std::string UTF8String)

  3. accetta che un tale oggetto UTF8String sia solo un contenitore stupido, ma economico. Non accedere mai e / o manipolare mai direttamente i personaggi (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! (Bene, ci sono scenari in cui ha senso ... basta usare la libreria ICU per quelli).

  4. usa std :: wstring per stringhe codificate UCS-2 ( typedef std::wstring UCS2String) - questo è un compromesso e una concessione al caos introdotto dall'API WIN32). UCS-2 è sufficiente per la maggior parte di noi (ne parleremo più avanti ...).

  5. utilizzare le istanze UCS2String ogni volta che è richiesto un accesso carattere per carattere (lettura, manipolazione e così via). Qualsiasi elaborazione basata sui caratteri deve essere eseguita in una rappresentazione NON multibyte. È semplice, veloce, facile.

  6. 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. Utilizzare UTF8String ovunque la memoria sia preziosa e per tutti gli I / O UTF-8. Utilizzare UCS2String laddove la stringa debba essere analizzata e / o manipolata. È possibile convertire tra queste due rappresentazioni in qualsiasi momento.

Alternative e miglioramenti

  • le conversioni da e verso le codifiche di caratteri a byte singolo (ad es. ISO-8859-1) possono essere realizzate con l'aiuto di semplici tabelle di traduzione, ad es. const wchar_t tt_iso88951[256] = {0,1,2,...};e codice appropriato per la conversione da e verso UCS2.

  • se UCS-2 non è sufficiente, passare a UCS-4 ( typedef std::basic_string<uint32_t> UCS2String)

ICU o altre librerie unicode?

Per cose avanzate.


Dang, non è bene sapere che il supporto Unicode nativo non c'è.
Mihai Danila,

@Frunsi, sono curioso di sapere se hai provato Glib :: ustring e, in tal caso, quali sono i tuoi pensieri?
Caroline Beltran,

@CarolineBeltran: Conosco Glib, ma non l'ho mai usato, e probabilmente non lo userò nemmeno, perché è piuttosto limitato a una piattaforma target piuttosto non specifica (sistemi unixoid ...). La sua porta di Windows si basa sul livello esterno win2unix e in questo caso IMHO non ha alcun livello di compatibilità OSX. Tutta questa roba sta indirizzando chiaramente in una direzione sbagliata, almeno per il mio codice (a questo livello di arco ...) ;-) Quindi, Glib non è un'opzione
Frunsi,

9
Cerca, sostituisci e così via funziona perfettamente sulle stringhe UTF-8 (una parte della sequenza di byte che rappresenta un carattere non può mai essere interpretata erroneamente come un altro carattere). In effetti, UTF-16 e UTF-32 non rendono affatto più facile questo: tutte e tre le codifiche sono in pratica codifiche multibyte, perché un personaggio percepito dall'utente (grapheme cluster) può essere lungo un numero qualsiasi di punti di codice unicode! La soluzione pragmatica è utilizzare UTF-8 per tutto e convertirlo in UTF-16 solo quando si ha a che fare con l'API di Windows.
Daniel,

5
@Frunsi: la ricerca e la sostituzione funzionano altrettanto bene con UTF-8 come con UTF-32. È proprio perché una corretta elaborazione del testo compatibile con Unicode deve comunque gestire i 'caratteri' multi-codice, che l'uso di una codifica a lunghezza variabile come UTF-8 non rende l'elaborazione delle stringhe più complicata. Quindi usa UTF-8 ovunque. Le normali funzioni della stringa C funzioneranno bene su UTF-8 (e corrisponderanno ai confronti ordinali sulla stringa Unicode), e se hai bisogno di qualcosa di più sensibile al linguaggio, dovrai comunque chiamare una libreria Unicode, UTF-16/32 non posso salvarti da quello.
Daniel,

25
  1. Quando si desidera avere caratteri ampi memorizzati nella stringa. widedipende dall'implementazione. Visual C ++ viene impostato per impostazione predefinita su 16 bit se ricordo correttamente, mentre GCC viene impostato automaticamente in base alla destinazione. È lungo 32 bit qui. Nota wchar_t (tipo di carattere largo) non ha nulla a che fare con unicode. È semplicemente garantito che può archiviare tutti i membri del set di caratteri più grande supportato dall'implementazione in base alle impostazioni locali e almeno fino a quando char. Puoi anche archiviare bene le stringhe unicode std::stringusando la utf-8codifica. Ma non capirà il significato dei punti di codice unicode. Così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 / stringa. Per questa ragione, la gente del wrapper C ++ gtk / glib ha sviluppato una Glib::ustringclasse in grado di gestire utf-8.

    Se wchar_t è lungo 32 bit, è possibile utilizzare utf-32come codifica unicode e è possibile memorizzare e gestire stringhe unicode utilizzando una codifica fissa (utf-32 è a lunghezza fissa). Ciò significa che la s.size()funzione del tuo wstring restituirà quindi la giusta quantità di elementi wchar_t e caratteri logici.

  2. Sì, char è sempre lungo almeno 8 bit, il che significa che può memorizzare tutti i valori ASCII.
  3. Sì, tutti i principali compilatori lo supportano.

Sono curioso di sapere # 2. Ho pensato che anche 7 bit sarebbero stati tecnicamente validi? O è necessario essere in grado di archiviare qualsiasi cosa oltre i caratteri ASCII a 7 bit?
jalf

1
si, jalf. c89 specifica intervalli minimi per i tipi base nella sua documentazione dei limiti. h (per caratteri non firmati, che è 0..255 min) e un sistema binario puro per i tipi interi. segue char, char senza segno e char con lunghezza minima dei bit di 8. c ++ eredita tali regole.
Johannes Schaub - litb

15
"Questo significa che la funzione s.size () di wstring restituirà quindi la giusta quantità di elementi wchar_t e caratteri logici." Questo non è del tutto accurato, nemmeno per Unicode. Sarebbe più preciso dire punto di codice che "carattere logico", anche in UTF-32 un dato carattere può essere composto da più punti di codice.
Logan Capaldo,

In sostanza state dicendo che C ++ non ha il supporto nativo per il set di caratteri Unicode?
Mihai Danila,

1
"Ma non capirà il significato dei punti di codice unicode." Su Windows, neanche std::wstring.
Deduplicatore

5

Uso frequentemente std :: string per contenere i caratteri utf-8 senza alcun problema. Consiglio vivamente di farlo quando si interfaccia con API che usano anche utf-8 come tipo di stringa nativo.

Ad esempio, utilizzo utf-8 per l'interfacciamento del mio codice con l'interprete Tcl.

L'avvertenza principale è la lunghezza dello std :: string, non è più il numero di caratteri nella stringa.


1
Juan: Vuoi dire che std :: string può contenere tutti i caratteri unicode ma la lunghezza riporterà in modo errato? C'è un motivo per cui segnala una lunghezza errata?

3
Quando si utilizza la codifica utf-8, un singolo carattere unicode può essere composto da più byte. Questo è il motivo per cui la codifica utf-8 è più piccola quando si usano principalmente caratteri del set ASCII standard. È necessario utilizzare funzioni speciali (o scorrere le proprie) per misurare il numero di caratteri Unicode.

2
(Specifico per Windows) La maggior parte delle funzioni prevede che una stringa che utilizza byte sia ASCII e 2 byte sia Unicode, versioni precedenti MBCS. Ciò significa che se si sta memorizzando unicode a 8 bit, sarà necessario convertirlo in unicode a 16 bit per chiamare una funzione Windows standard (a meno che non si stia utilizzando solo la porzione ASCII).
Greg Domjan,

2
Non solo una stringa std :: string riporterà la lunghezza in modo errato, ma genererà anche la stringa errata. Se alcuni caratteri Unicode sono rappresentati in UTF-8 come byte multipli, che std :: string considera i propri caratteri, allora le vostre routine di manipolazione tipicamente std :: string probabilmente produrranno i numerosi strani caratteri che risultano dall'errata interpretazione di quello carattere corretto.
Mihai Danila,

2
Suggerisco di cambiare la risposta per indicare che le stringhe dovrebbero essere considerate solo come contenitori di byte e, se i byte hanno una codifica Unicode (UTF-8, UTF-16, ...), è necessario utilizzare librerie specifiche che comprendano quello. Le API standard basate su stringhe (lunghezza, substr, ecc.) Falliranno miseramente con i caratteri multibyte. Se viene effettuato questo aggiornamento, rimuoverò il mio downvote.
Mihai Danila,

4
  1. Quando si desidera memorizzare caratteri "larghi" (Unicode).
  2. Sì: 255 (escluso 0).
  3. Sì.
  4. Ecco un articolo introduttivo: http://www.joelonsoftware.com/articles/Unicode.html

11
std :: string può contenere 0 bene (fai attenzione se chiami il metodo c_str ())
Mr Fooz,

3
E a rigor di termini, non è garantito che un carattere sia di 8 bit. :) Il tuo link in # 4 è assolutamente da leggere, ma non credo che risponda alla domanda. Un personaggio ampio non ha assolutamente nulla a che fare con unicode. È semplicemente un personaggio più ampio. (Quanto più ampio dipende dal sistema operativo, ma in genere 16 o 32 bit)
jalf

2
  1. quando si desidera utilizzare stringhe Unicode e non solo ASCII, utile per l'internazionalizzazione
  2. si, ma non gioca bene con 0
  3. ignaro di quelli che non lo fanno
  4. wide character è il modo specifico del compilatore di gestire la rappresentazione a lunghezza fissa di un carattere unicode, per MSVC è un carattere a 2 byte, per gcc ho capito che è di 4 byte. e un +1 per http://www.joelonsoftware.com/articles/Unicode.html

1
2. Una stringa std :: string può contenere bene un carattere NULL. Può contenere anche caratteri utf-8 e ampi.

@Juan: Questo mi ha fatto confondere di nuovo. Se std :: string può mantenere i caratteri unicode, cosa c'è di speciale in std :: wstring?

1
@Appu: std :: string può contenere caratteri unicode UTF-8. Esistono numerosi standard unicode destinati a diverse larghezze di caratteri. UTf8 ha una larghezza di 8 bit. C'è anche UTF-16 e UTF-32 rispettivamente a 16 e 32 bit di larghezza
Greg D

Con uno std :: wstring. Ogni carattere unicode può essere un wchar_t quando si usano codifiche a lunghezza fissa. Ad esempio, se si sceglie di utilizzare l'approccio joel on software come collegamento Greg. Quindi la lunghezza della stringa è esattamente il numero di caratteri unicode nella stringa. Ma occupa più spazio

Non ho detto che non poteva contenere uno 0 '\ 0', e ciò che intendevo dire non gioca bene è che alcuni metodi potrebbero non darti un risultato atteso contenente tutti i dati del wstring. Così duro con i voti negativi.
Greg Domjan,

2

Le applicazioni che non sono soddisfatte solo con 256 caratteri diversi hanno la possibilità di utilizzare caratteri ampi (più di 8 bit) o ​​una codifica a lunghezza variabile (una codifica multibyte nella terminologia C ++) come UTF-8. I caratteri larghi generalmente richiedono più spazio di una codifica a lunghezza variabile, ma sono più veloci da elaborare. Le applicazioni multilingue che elaborano grandi quantità di testo di solito utilizzano caratteri ampi durante l'elaborazione del testo, ma lo convertono in UTF-8 quando lo memorizzano su disco.

L'unica differenza tra a stringe a wstringè il tipo di dati dei caratteri che memorizzano. Una stringa memorizza chars la cui dimensione è garantita di almeno 8 bit, quindi è possibile 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 utilizza un set di caratteri i cui primi 128 caratteri corrispondono a ASCII. Questo è anche il caso dei compilatori che usano la codifica UTF-8. La cosa importante da tenere presente quando si usano stringhe in UTF-8 o 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 almeno grande quanto un carattere, di solito 16 bit o 32 bit. wstring può essere utilizzato per elaborare il testo nella codifica a caratteri grandi definita dall'implementazione. Poiché la codifica non è definita nello standard, non è semplice convertire tra stringhe e stringhe. Non si può presumere che anche le stringhe abbiano una codifica a lunghezza fissa.

Se non hai bisogno del supporto multilingue, potresti usare bene solo stringhe regolari. D'altra parte, se stai scrivendo un'applicazione grafica, spesso l'API supporta solo caratteri di grandi dimensioni. Quindi probabilmente si desidera utilizzare gli stessi caratteri larghi durante l'elaborazione del testo. Tieni presente che UTF-16 è una codifica di lunghezza variabile, il che significa che non puoi assumere length()di restituire il numero di caratteri. Se l'API utilizza una codifica a lunghezza fissa, come UCS-2, l'elaborazione diventa semplice. La conversione tra caratteri ampi e UTF-8 è difficile da eseguire in modo portatile, ma, di nuovo, l'API dell'interfaccia utente probabilmente supporta la conversione.


Quindi, parafrasando il primo paragrafo: l'applicazione che richiede più di 256 caratteri deve utilizzare una codifica multibyte o una codifica maybe_multibyte.
Deduplicatore,

In genere, tuttavia, le codifiche a 16 e 32 bit come UCS-2 e UCS-4 non sono chiamate codifiche multibyte. Lo standard C ++ distingue tra codifiche multibyte e caratteri ampi. Una rappresentazione di caratteri ampi utilizza un numero fisso (generalmente più di 8) bit per carattere. Le codifiche che utilizzano un singolo byte per codificare i caratteri più comuni e più byte per codificare il resto del set di caratteri, sono denominate codifiche multibyte.
Seppo Enarvi,

Scusa, commento sciatto. Avrebbe dovuto dire la codifica a lunghezza variabile. UTF-16 è una codifica a lunghezza variabile, proprio come UTF-8. Far finta di no non è una cattiva idea.
Deduplicatore,

È un buon punto. Non c'è motivo per cui wstrings non possa essere usato per memorizzare UTF-16 (invece di UCS-2), ma poi si perde la comodità di una codifica a lunghezza fissa.
Seppo Enarvi,

2

Una bella domanda Penso che ENCODING DEI DATI (a volte coinvolto anche un CHARSET ) sia un MECCANISMO DI ESPRESSIONE DELLA MEMORIA al fine di salvare i dati in un file o trasferire i dati tramite una 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 byte singolo e vogliamo elaborare o analizzare alcuni dati Unicode, ad esempio letti dal file Windows..REG o flusso di rete a 2 byte, dovremmo dichiarare facilmente la variabile std :: wstring elaborali. es: wstring ws = L "中国 a" (memoria a 6 ottetti: 0x4E2D 0x56FD 0x0061), possiamo usare ws [0] per ottenere il carattere '中' e ws [1] per ottenere il carattere '国' e ws [2] per ottieni il carattere "a", ecc.

2. Può std :: string contenere l'intero set di caratteri ASCII, inclusi i caratteri speciali?

Sì. Ma attenzione: American ASCII, significa che ogni ottetto 0x00 ~ 0xFF rappresenta un carattere, incluso un testo stampabile come "123abc & * _ &" e tu hai detto uno speciale, per lo più stampalo come '.' evitare di confondere editor o terminali. E alcuni altri paesi estendono il proprio set di caratteri "ASCII", ad esempio cinese, usano 2 ottetti per indicare un personaggio.

3.Std :: wstring è supportato da tutti i compilatori C ++ più diffusi?

Forse, o soprattutto. Ho usato: VC ++ 6 e GCC 3.3, SÌ

4. Che cos'è esattamente un "personaggio ampio"?

un carattere largo indica principalmente l'uso di 2 ottetti o 4 ottetti per contenere i personaggi di tutti i paesi. 2 ottetti UCS2 è un campione rappresentativo e, ad esempio, l'inglese 'a', la sua memoria è di 2 ottetti di 0x0061 (rispetto alla memoria ASCII 'a è di 1 ottetto 0x61)


0

Ci sono alcune ottime risposte qui, ma penso che ci siano un paio di cose che posso aggiungere riguardo a Windows / Visual Studio. Questa è basata sulla mia esperienza con VS2015. Su Linux, fondamentalmente la risposta è usare UTF-8 codificato std::stringovunque. Su Windows / VS diventa più complesso. Ecco perché. Windows prevede che le stringhe memorizzate utilizzando chars vengano codificate utilizzando la tabella codici locale. Questo è quasi sempre il set di caratteri ASCII seguito da altri 128 caratteri speciali a seconda della posizione. Vorrei solo affermare che questo non solo quando si utilizza l'API di Windows, ci sono altri tre luoghi principali in cui queste stringhe interagiscono con il C ++ standard. Si tratta di valori letterali di stringa, output per l' std::coututilizzo <<e il passaggio di un nome file astd::fstream .

Sarò qui di fronte che sono un programmatore, non uno specialista delle lingue. Apprezzo che USC2 e UTF-16 non siano uguali, ma per i miei scopi sono abbastanza vicini da essere intercambiabili e li uso come tali qui. In realtà non sono sicuro di quale Windows utilizzi, ma in genere non ho nemmeno bisogno di sapere. Ho dichiarato UCS2 in questa risposta, quindi scusami in anticipo se ho turbato qualcuno con la mia ignoranza su questa faccenda e sono felice di cambiarla se ho qualcosa di sbagliato.

Letterali a corda

Se inserisci valori letterali stringa che contengono solo caratteri che possono essere rappresentati dalla tua tabella codici, VS li memorizza nel tuo file con 1 byte per codifica dei caratteri in base alla tua tabella codici. Nota che se cambi la tua tabella codici o dai la tua fonte a un altro sviluppatore usando una tabella codici diversa, penso (ma non ho testato) che il personaggio finirà per essere diverso. Se esegui il codice su un computer utilizzando una tabella codici diversa, non sono sicuro che anche il personaggio cambierà.

Se inserisci valori letterali stringa che non possono essere rappresentati dalla tua tabella codici, VS ti chiederà di salvare il file come Unicode. Il file verrà quindi codificato come UTF-8. Ciò significa che tutti i caratteri non ASCII (compresi quelli presenti nella tua tabella codici) saranno rappresentati da 2 o più byte. Ciò significa che se dai la tua fonte a qualcun altro, la fonte avrà lo stesso aspetto. Tuttavia, prima di passare l'origine al compilatore, VS converte il testo codificato UTF-8 nel testo codificato della tabella codici e tutti i caratteri mancanti dalla tabella codici vengono sostituiti con ?.

L'unico modo per garantire la corretta rappresentazione di un valore letterale stringa Unicode in VS è quello di precedere il valore letterale stringa Lrendendolo un valore letterale stringa ampio. In questo caso VS convertirà il testo codificato UTF-8 dal file in UCS2. È quindi necessario passare letteralmente questa stringa in un std::wstringcostruttore o convertirla in utf-8 e metterla in a std::string. Oppure, se lo desideri, puoi utilizzare le funzioni dell'API di Windows per codificarlo utilizzando la tua tabella codici per inserirlo in a std::string, ma potresti non aver usato una stringa letterale ampia.

std :: cout

Quando si esegue l'output alla console mediante <<è possibile utilizzare solo std::string, no std::wstringe il testo deve essere codificato utilizzando la tabella codici locale. Se ne hai uno std::wstring, devi convertirlo utilizzando una delle funzioni dell'API di Windows e tutti i caratteri non presenti nella tua tabella codici vengono sostituiti da ?(forse puoi cambiare il carattere, non ricordo).

std :: nomi di file fstream

Il sistema operativo Windows utilizza UCS2 / UTF-16 per i suoi nomi di file, quindi qualunque sia la tua tabella codici, puoi avere file con qualsiasi carattere Unicode. Ciò significa che per accedere o creare file con caratteri non presenti nella tabella codici è necessario utilizzare std::wstring. Non c'è altro modo. Questa è un'estensione specifica di Microsoft, std::fstreamquindi probabilmente non verrà compilata su altri sistemi. Se usi std :: string, puoi utilizzare solo nomi di file che includono solo caratteri nella tua tabella codici.

Le tue opzioni

Se stai solo lavorando su Linux, probabilmente non sei arrivato così lontano. Usa UTF-8 std::stringovunque.

Se stai solo lavorando su Windows, usa UCS2 std::wstringovunque. Alcuni puristi potrebbero dire che utilizzare UTF8 e poi convertirli quando necessario, ma perché preoccuparsi della seccatura.

Se sei multipiattaforma, allora è un casino essere sincero. Se si tenta di utilizzare UTF-8 ovunque su Windows, è necessario prestare molta attenzione ai valori letterali delle stringhe e all'output sulla console. Puoi facilmente corrompere le tue corde lì. Se usi std::wstringovunque su Linux, potresti non avere accesso alla versione estesa di std::fstream, quindi devi fare la conversione, ma non c'è rischio di corruzione. Quindi personalmente penso che questa sia un'opzione migliore. Molti non sarebbero d'accordo, ma io non sono solo - è il percorso intrapreso da wxWidgets per esempio.

Un'altra opzione potrebbe essere quella di digitare unicodestringcome std::stringsu Linux e std::wstringsu Windows e avere una macro chiamata UNI () che prefigura L su Windows e nulla su Linux, quindi il codice

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

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

andrebbe bene su entrambe le piattaforme credo.

risposte

Quindi, per rispondere alle tue domande

1) Se stai programmando per Windows, quindi sempre, se multipiattaforma, forse sempre, a meno che tu non voglia affrontare eventuali problemi di corruzione su Windows o scrivere un codice con una piattaforma specifica #ifdefsper aggirare le differenze, se stai semplicemente usando Linux quindi mai.

2) Sì. Inoltre su Linux puoi usarlo anche per tutti gli Unicode. Su Windows puoi usarlo per tutti gli Unicode se scegli di codificare manualmente usando UTF-8. Ma l'API di Windows e le classi C ++ standard si aspettano std::stringche vengano codificate utilizzando la tabella codici locale. Ciò include tutte le ASCII più altri 128 caratteri che cambiano a seconda della tabella codici che il computer è configurato per l'uso.

3) Credo di sì, ma in caso contrario si tratta solo di un semplice typedef di un 'std :: basic_string' che utilizza wchar_tinvece dichar

4) Un carattere largo è un tipo di carattere più grande del chartipo standard a 1 byte . Su Windows è di 2 byte, su Linux è di 4 byte.


1
Per quanto riguarda "Tuttavia, prima di passare la fonte al compilatore, VS converte il testo codificato UTF-8 in testo codificato nella tabella codici e tutti i caratteri mancanti dalla tabella codici vengono sostituiti con?." -> Non penso che ciò sia vero quando il compilatore utilizza la codifica UTF-8 (uso /utf-8).
Roi Danton,

Non ne ero a conoscenza come opzione. Da questo link docs.microsoft.com/en-us/cpp/build/reference/… sembra che non ci sia alcuna casella di spunta da selezionare nelle proprietà del progetto, è necessario aggiungerlo come opzione aggiuntiva da riga di comando. Buon posto!
Phil Rosenberg,


-6

Quando NON dovresti usare caratteri ampi?

Quando scrivi il codice prima dell'anno 1990.

Ovviamente, sto girando, ma in realtà è il 21 ° secolo ormai. 127 personaggi hanno smesso da tempo di essere sufficienti. Sì, puoi usare UTF8, ma perché preoccuparsi del mal di testa?


16
@dave: non so quale mal di testa crea UTF-8 che è maggiore di quello di Widechars (UTF-16). in UTF-16, hai anche personaggi multi-carattere.
Pavel Radzivilovsky,

Il problema è che se non sei altro che un paese di lingua inglese, DEVI utilizzare wchar_t. Per non parlare del fatto che alcuni alfabeti hanno molti più caratteri di quanti tu possa inserire in un byte. Eravamo lì, su DOS. Codepage schizofrenia, no, grazie, non di più ..
Swift - Friday Pie

1
@Swift Il problema wchar_tè che le dimensioni e il significato sono specifici del sistema operativo. Sostituisce i vecchi problemi con quelli nuovi. Considerando che a charè un charindipendentemente dal sistema operativo (almeno su piattaforme simili). Quindi potremmo anche usare UTF-8, mettere tutto in sequenze di chars, e lamentarci di come C ++ ci lasci completamente soli senza alcun metodo standard per misurare, indicizzare, trovare ecc all'interno di tali sequenze.
underscore_d

1
@Swift Sembra che tu l'abbia completamente indietro. wchar_tè un tipo di dati a larghezza fissa, quindi un array di 10 wchar_toccuperà sempre sizeof(wchar_t) * 10byte della piattaforma. E UTF-16 è una codifica a larghezza variabile in cui i caratteri possono essere costituiti da 1 o 2 punti di codice a 16 bit (e s / 16/8 / g per UTF-8).
underscore_d

1
@SteveHollasch La rappresentazione wchar_t della stringa su Windows codificherebbe i caratteri maggiori di FFFF come coppia surrogata aspeciale, mentre altri prenderebbero un solo elemento wchar_t. Quindi quella rappresentazione non sarà compatibile con la rappresentazione creata dal compilatore gnu (dove tutti i caratteri inferiori a FFFF avranno zero parole davanti a loro). Ciò che è memorizzato in wchar_t è determinato dal programmatore e dal compilatore, non da un accordo
Swift - Friday Pie
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.