2010-11-12 17 views
11

Così ho finalmente ottenuto di nuovo al mio compito principale - porting piuttosto grande progetto C++ da Windows a Mac.stringhe multipiattaforma (e Unicode) in C++

Subito Sono stato colpito dal problema in cui wchar_t è di 16-bit per Windows, ma a 32-bit su Mac. Questo è un problema perché tutte le stringhe sono rappresentate da wchar_t e ci saranno dati di stringhe che vanno avanti e indietro tra macchine Windows e Mac (sia nei dati su disco sia nei moduli dati di rete). A causa del modo in cui funziona, non sarebbe del tutto semplice convertire le stringhe in un formato comune prima di inviare e ricevere i dati.

Abbiamo anche iniziato a supportare molte più lingue di recente e quindi stiamo iniziando a gestire molti dati Unicode (oltre a trattare con le lingue da destra a sinistra).

Ora, potrei essere una confusione fra molteplici idee qui e causando più problemi per me del necessario ed è per questo che sto chiedendo a questa domanda. Pensiamo che memorizzare tutti i dati delle stringhe in memoria come UTF-8 abbia molto senso. Risolve il problema con wchar_t di dimensioni diverse, significa che possiamo facilmente supportare più lingue e riduce drasticamente anche il nostro footprint di memoria (abbiamo un sacco di - per lo più in inglese - stringhe caricate) - ma non sembra che molte persone stiano facendo Questo. C'è qualcosa che ci manca? C'è il problema ovvio che devi affrontare in cui la lunghezza della stringa può essere inferiore alla dimensione della memoria che memorizza i dati della stringa.

O sta usando UTF-16 un'idea migliore? O dovremmo attenerci a wchar_t e scrivere codice per convertire tra wchar_t e, diciamo, Unicode in posti dove leggiamo/scriviamo sul disco o sulla rete?

Mi rendo conto che è pericolosamente vicino a chiedere pareri - ma siamo nervosi che stiamo trascurando qualcosa di ovvio perché non sembra che ci siano molte classi di stringhe Unicode (per esempio) - ma tuttavia c'è un sacco di codice per la conversione in/da Unicode come in boost :: locale, iconv, utf-cpp e ICU.

+0

solo una parola da dire. http://utf8everywhere.org –

risposta

7

Utilizzare sempre un protocollo definito nel byte quando è coinvolto un file o una connessione di rete. Non fare affidamento su come un compilatore C++ memorizza qualcosa in memoria. Per il testo Unicode, questo significa scegliere sia una codifica sia un ordine di byte (okay, UTF-8 non si preoccupa dell'ordinamento dei byte). Anche se le piattaforme che attualmente si desidera supportare hanno architetture simili, probabilmente verrà un'altra piattaforma popolare con un comportamento diverso o persino un nuovo sistema operativo per una delle piattaforme esistenti, e sarete contenti di aver scritto codice portatile.

1

Come regola empirica: UTF-16 per l'elaborazione, UTF-8 per la comunicazione & stoccaggio.

Certo, qualsiasi regola può essere rotto e questo non è scolpito nella pietra. Ma devi sapere quando è giusto romperlo.

Per esempio potrebbe essere una buona idea di utilizzare qualcosa di diverso se l'ambiente si sta utilizzando vuole qualcos'altro. Ma le API Mac OS X utilizzano UTF-16, come per Windows. Quindi UTF-16 ha più senso. È più semplice convertire prima di mettere/ottenere cose in rete (perché probabilmente lo fai in 2-3 routine) piuttosto che fare tutte le conversioni per chiamare le API del SO.

Indica anche il tipo di applicazione che sviluppi. Se si tratta di qualcosa con pochissima elaborazione del testo e pochissime chiamate al sistema (qualcosa come un server di posta elettronica che sposta principalmente le cose senza cambiarle), allora UTF-8 potrebbe essere una buona scelta.

Quindi, per quanto tu possa odiare questa risposta, "dipende".

2

Tendo ad utilizzare UTF-8 come rappresentazione interna. Si perde solo il controllo della lunghezza della stringa, ma non è comunque molto utile. Per la conversione dell'API di Windows, utilizzo le mie funzioni di conversione Win32 I devised here. Come Mac e Linux sono (per la maggior parte parte standard UTF-8-aware, per la non c'è bisogno di convertire nulla lì). I bonus gratuiti si ottengono:

  1. utilizzare semplice std::string vecchio.
  2. trasporto di rete/flusso a byte.
  3. Per la maggior parte delle lingue, buona memoria.
  4. Per ulteriori funzionalità: utf8cpp
+3

UTF-8 ** non ** consente di utilizzare "plain old' std :: string' ". Forse se tutto ciò che si vuole fare è memorizzare la stringa che va bene, ma non si può effettivamente modificare la stringa in quel modulo senza scrivere la propria spazzatura di elaborazione UTF-8 se si utilizza quel contenitore. (cioè non puoi usare funzioni membro come 'std :: string :: find' e aspettarti che funzionino correttamente con le stringhe UTF-8) Troppe persone pensano" Oh, userò semplicemente UTF-8 "e penso che possano solo continuare a trattare tutto come array di caratteri, che è falso. –

+5

@Billy: questo è vero per qualsiasi codifica multibyte. std :: string è un contenitore di caratteri, non di glifi ed è perfettamente corretto mantenere il testo con codifica UTF-8 in std :: string ed elaborarlo con qualcosa come utf8cpp –

+2

@Nemanja: Sì, va bene usare uno std :: stringa per l'archiviazione, ma tecnicamente potresti * memorizzare * qualsiasi cosa in una stringa std :: (a patto che tu possa fornire un faccetto fittizio 'std :: char_traits' per esso). Tuttavia, quando si dice "È possibile utilizzare plain old std :: string", si presume che possano effettivamente utilizzare la classe per qualcosa di diverso dall'archiviazione dei dati. Se ** solo storage ** è ciò che stai cercando, allora dovresti probabilmente usare 'vector'. –

0

ICU ha una classe string C++, UnicodeString

+1

ICU è una bella libreria per questo tipo di cose. Sfortunatamente è anche ** enorme ** (la dimensione compilata di ICU è di circa 25 MB). In alcuni casi potrebbe andar bene, ma in altri non è (ovviamente) corretto. Alcune persone in realtà non hanno bisogno di tutte le funzionalità che fornisce. OTOH, chiunque implementa quello che fa di solito si sbaglia di solito (cose come le regole di confronto sono diverse per locale e ICU gestisce quella roba correttamente) –

+0

Gran parte di ciò sono dati per 500 locali e centinaia di convertitori e tutte le possibili librerie. È abbastanza facilmente personalizzabile dal punto di vista dei dati e del codice, se non hai bisogno di tutto. La libreria core icuuc per esempio è di circa 1,4 MB, esclusi i dati. –