UTF-16 dovrebbe essere considerato dannoso?


432

Farò una domanda probabilmente controversa: "Una delle codifiche più popolari, UTF-16, dovrebbe essere considerata dannosa?"

Perché faccio questa domanda?

Quanti programmatori sono consapevoli del fatto che UTF-16 è in realtà una codifica a lunghezza variabile? Con questo intendo dire che ci sono punti di codice che, rappresentati come coppie surrogate, accettano più di un elemento.

Lo so; molte applicazioni, framework e API usano UTF-16, come String di Java, String di C #, API Win32, librerie Qt GUI, libreria Unicode ICU, ecc. Tuttavia, nonostante tutto, ci sono molti bug di base nell'elaborazione di caratteri al di fuori di BMP (caratteri che devono essere codificati utilizzando due elementi UTF-16).

Ad esempio, prova a modificare uno di questi caratteri:

  • 𝄞 ( U + 1D11E ) SIMBOLO MUSICALE G CLEF
  • 𝕥 ( U + 1D565 ) MATEMATICO DOPPIO CAMION PICCOLO T
  • 𝟶 ( U + 1D7F6 ) ZERO DI MONOSPACE MATEMATICO
  • 𠂊 ( U + 2008A ) Personaggio Han

Potresti perdere alcuni, a seconda dei caratteri che hai installato. Questi personaggi sono tutti al di fuori del BMP (Basic Multilingual Plane). Se non riesci a vedere questi personaggi, puoi anche provare a guardarli nel riferimento Carattere Unicode .

Ad esempio, prova a creare nomi di file in Windows che includano questi caratteri; prova a eliminare questi caratteri con uno "spazio indietro" per vedere come si comportano in diverse applicazioni che usano UTF-16. Ho fatto alcuni test e i risultati sono piuttosto negativi:

  • Opera ha problemi con la loro modifica (elimina le 2 pressioni richieste sul backspace)
  • Il Blocco note non è in grado di gestirli correttamente (eliminare le 2 pressioni richieste sul backspace)
  • Modifica dei nomi dei file nelle finestre di dialogo interrotte (eliminazione delle 2 pressioni richieste sul backspace)
  • Tutte le applicazioni QT3 non possono gestirle: mostra due quadrati vuoti invece di un simbolo.
  • Python codifica questi caratteri in modo errato se utilizzato direttamente u'X'!=unicode('X','utf-16')su alcune piattaforme quando X in carattere al di fuori di BMP.
  • L'unicodedata di Python 2.5 non riesce a ottenere proprietà su tali caratteri quando python viene compilato con stringhe Unicode UTF-16.
  • StackOverflow sembra rimuovere questi caratteri dal testo se modificato direttamente come caratteri Unicode (questi caratteri sono mostrati usando escape Unicode HTML).
  • WinForms TextBox può generare una stringa non valida se limitata con MaxLength.

Sembra che tali bug siano estremamente facili da trovare in molte applicazioni che utilizzano UTF-16.

Quindi ... Pensi che UTF-16 debba essere considerato dannoso?


64
Non proprio corretto. Spiego, se scrivi "שָׁ" il carattere composto costituito da "ש", "ָ" e "ׁ", i vovels, quindi la rimozione di ognuno di essi è logica, rimuovi un punto di codice quando premi " backspace "e rimuovi tutti i caratteri inclusi i vovel quando premi" del ". Ma non produci mai stato illegale di testo - punti di codice illegali. Pertanto, la situazione quando si preme backspace e si ottiene il testo illegale non è corretta.

41
CiscoIPPhone: se un bug viene "segnalato più volte, da molte persone diverse", e poi un paio di anni dopo uno sviluppatore scrive su un blog di sviluppo che "Ci crediate o no, il comportamento è per lo più intenzionale!", Quindi (per dirla leggermente) Tendo a pensare che probabilmente non è la migliore decisione di progettazione mai presa. :-) Solo perché è intenzionale non significa che non sia un bug.

145
Ottimo post. UTF-16 è davvero il "peggio di entrambi i mondi": UTF8 è di lunghezza variabile, copre tutto Unicode, richiede un algoritmo di trasformazione da e verso punti di codice grezzi, limita a ASCII e non ha problemi di endianness. UTF32 è a lunghezza fissa, non richiede trasformazione, ma occupa più spazio e presenta problemi di endianness. Fin qui tutto bene, puoi usare UTF32 internamente e UTF8 per la serializzazione. Ma UTF16 non ha vantaggi: dipende dall'endian, ha una lunghezza variabile, occupa molto spazio, non è compatibile con ASCII. Lo sforzo necessario per gestire correttamente UTF16 potrebbe essere speso meglio su UTF8.
Kerrek SB,

26
@Ian: UTF-8 NON ha gli stessi avvertimenti di UTF-8. Non puoi avere surrogati in UTF-8. UTF-8 non si maschera da qualcosa che non lo è, ma la maggior parte dei programmatori che usano UTF-16 lo usano in modo sbagliato. Lo so. Li ho guardati ancora e ancora e ancora e ancora.
tchrist,

18
Inoltre, UTF-8 non ha il problema perché tutti lo trattano come una codifica a larghezza variabile. Il motivo per cui UTF-16 ha il problema è perché tutti lo trattano come una codifica a larghezza fissa.
Christoffer Hammarström,

Risposte:


340

Questa è una vecchia risposta.
Vedi UTF-8 Ovunque per gli ultimi aggiornamenti.

Opinione: Sì, UTF-16 dovrebbe essere considerato dannoso . Il vero motivo esiste perché qualche tempo fa c'era una convinzione errata che Widechar sarebbe quello che è UCS-4 ora.

Nonostante l '"anglo-centrismo" di UTF-8, dovrebbe essere considerato l'unica codifica utile per il testo. Si può sostenere che non dovrebbero mai esistere codici sorgente di programmi, pagine Web e file XML, nomi di file del sistema operativo e altre interfacce di testo da computer a computer. Ma quando lo fanno, il testo non è solo per i lettori umani.

D'altra parte, l'overhead UTF-8 è un piccolo prezzo da pagare mentre presenta vantaggi significativi. Vantaggi come la compatibilità con il codice inconsapevole che passa semplicemente con le stringhe char*. Questa è una grande cosa. Ci sono alcuni personaggi utili che sono PIÙ CORTI in UTF-16 rispetto a UTF-8.

Credo che alla fine moriranno tutte le altre codifiche. Ciò implica che MS-Windows, Java, ICU e Python smettano di usarlo come preferito. Dopo lunghe ricerche e discussioni, le convenzioni di sviluppo presso la mia azienda vietano l'utilizzo di UTF-16 ovunque tranne le chiamate API OS, e questo nonostante l'importanza delle prestazioni nelle nostre applicazioni e il fatto che utilizziamo Windows. Le funzioni di conversione sono state sviluppate per convertire gli UTF8 sempre assunti std::stringin UTF-16 nativo, che Windows stesso non supporta correttamente .

Alle persone che dicono " usa ciò che è necessario dove è necessario ", dico: c'è un enorme vantaggio nell'usare la stessa codifica ovunque, e non vedo motivi sufficienti per fare diversamente. In particolare, penso che l'aggiunta wchar_ta C ++ sia stata un errore, così come lo sono le aggiunte Unicode a C ++ 0x. Ciò che deve essere richiesto dalle implementazioni STL è che ogni std::stringo char*parametro sarebbe considerato compatibile con unicode.

Sono anche contrario all'approccio " usa quello che vuoi ". Non vedo alcun motivo per tale libertà. C'è abbastanza confusione sull'argomento del testo, risultante in tutto questo software rotto. Detto questo, sono convinto che i programmatori debbano finalmente raggiungere il consenso su UTF-8 come un modo corretto. (Vengo da un paese di lingua non ascii e sono cresciuto su Windows, quindi mi sarei aspettato per ultimo di attaccare UTF-16 per motivi religiosi).

Vorrei condividere maggiori informazioni su come scrivo il testo su Windows e su ciò che consiglio a tutti gli altri per la correttezza unicode verificata in fase di compilazione, la facilità d'uso e una migliore multipiattaforma del codice. Il suggerimento differisce sostanzialmente da quello che di solito è raccomandato come il modo corretto di usare Unicode su Windows. Tuttavia, la ricerca approfondita di queste raccomandazioni ha portato alla stessa conclusione. Quindi ecco qui:

  • Non utilizzare wchar_to std::wstringin luoghi diversi dal punto adiacente alle API che accettano UTF-16.
  • Non utilizzare _T("")o L""UTF-16 letterali (IMO Questi dovrebbero essere prese fuori dello standard, come una parte di UTF-16 disapprovazione).
  • Non utilizzare tipi, funzioni o loro derivati ​​sensibili alla _UNICODEcostante, come LPTSTRo CreateWindow().
  • Tuttavia, _UNICODEsempre definito, per evitare che le char*stringhe di passaggio a WinAPI vengano compilate silenziosamente
  • std::stringse in char*qualsiasi parte del programma sono considerati UTF-8 (se non diversamente specificato)
  • Tutte le mie stringhe sono std::string, anche se puoi passare char * o letter letter a convert(const std::string &).
  • usa solo le funzioni Win32 che accettano widechars ( LPWSTR). Mai quelli che accettano LPTSTRo LPSTR. Passare i parametri in questo modo:

    ::SetWindowTextW(Utils::convert(someStdString or "string litteral").c_str())
    

    (La politica utilizza le funzioni di conversione di seguito.)

  • Con stringhe MFC:

    CString someoneElse; // something that arrived from MFC. Converted as soon as possible, before passing any further away from the API call:
    
    std::string s = str(boost::format("Hello %s\n") % Convert(someoneElse));
    AfxMessageBox(MfcUtils::Convert(s), _T("Error"), MB_OK);
    
  • Lavorare con file, nomi di file e fstream su Windows:

    • Non passare mai std::stringo const char*argomenti del nome file alla fstreamfamiglia. MSVC STL non supporta gli argomenti UTF-8, ma ha un'estensione non standard che dovrebbe essere utilizzata come segue:
    • Converti gli std::stringargomenti in std::wstringcon Utils::Convert:

      std::ifstream ifs(Utils::Convert("hello"),
                        std::ios_base::in |
                        std::ios_base::binary);
      

      Dovremo rimuovere manualmente la conversione, quando cambierà l'atteggiamento di MSVC nei confronti fstream.

    • Questo codice non è multipiattaforma e potrebbe essere necessario modificarlo manualmente in futuro
    • Vedi fstreamcaso di ricerca / discussione unicode 4215 per maggiori informazioni.
    • Non produrre mai file di output di testo con contenuto non UTF8
    • Evitare l'uso fopen()per motivi RAII / OOD. Se necessario, utilizzare le _wfopen()convenzioni WinAPI sopra.

// For interface to win32 API functions
std::string convert(const std::wstring& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

std::wstring convert(const std::string& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

// Interface to MFC
std::string convert(const CString &mfcString)
{
#ifdef UNICODE
    return Utils::convert(std::wstring(mfcString.GetString()));
#else
    return mfcString.GetString();   // This branch is deprecated.
#endif
}

CString convert(const std::string &s)
{
#ifdef UNICODE
    return CString(Utils::convert(s).c_str());
#else
    Exceptions::Assert(false, "Unicode policy violation. See W569"); // This branch is deprecated as it does not support unicode
    return s.c_str();   
#endif
}

39
Non posso essere d'accordo. I vantaggi di utf16 rispetto a utf8 per molte lingue asiatiche dominano completamente i punti sollevati. È ingenuo sperare che i giapponesi, i thailandesi, i cinesi, ecc. Stiano per rinunciare a questa codifica. Gli scontri problematici tra i set di caratteri si verificano quando i set di caratteri sembrano per lo più simili, tranne che per le differenze. Suggerisco di standardizzare su: fisso 7 bit: iso-irv-170; Variabile a 8 bit: utf8; Variabile a 16 bit: utf16; 32 bit fisso: ucs4.

82
@Charles: grazie per il tuo contributo. È vero, alcuni caratteri BMP sono più lunghi in UTF-8 che in UTF-16. Ma ammettiamolo: il problema non è nei byte che prendono i caratteri cinesi BMP, ma la complessità del design del software che sorge. Se un programmatore cinese deve comunque progettare caratteri di lunghezza variabile, sembra che UTF-8 abbia ancora un piccolo prezzo da pagare rispetto ad altre variabili nel sistema. Potrebbe usare UTF-16 come algoritmo di compressione se lo spazio è così importante, ma anche in questo caso non ci sarà corrispondenza per LZ, e dopo che LZ o altra compressione generica avranno entrambe le stesse dimensioni ed entropia.

32
Quello che sostanzialmente dico è che la semplificazione offerta dall'avere una codifica compatibile anche con i programmi char * esistenti, ed è anche la più popolare oggi per tutto è inimmaginabile. È quasi come ai bei vecchi tempi "in chiaro". Vuoi aprire un file con un nome? Non c'è bisogno di preoccuparsi del tipo di unicode che stai facendo, ecc. Ecc. Suggerisco a noi sviluppatori di limitare UTF-16 a casi molto speciali di ottimizzazione severa in cui un minimo di prestazioni merita mesi-uomo di lavoro.

17
Linux ha avuto un requisito specifico nella scelta di utilizzare UTF-8 internamente: compatibilità con Unix. Windows non ne aveva bisogno, e quindi quando gli sviluppatori implementarono Unicode, aggiunsero le versioni UCS-2 di quasi tutte le funzioni che gestivano il testo e fecero semplicemente convertire quelle multibyte in UCS-2 e chiamarono le altre. THey sostituisce in seguito UCS-2 con UTF-16. Linux d'altra parte ha mantenuto le codifiche a 8 bit e quindi ha usato UTF-8, poiché è la scelta corretta in quel caso.
Mircea Chirea,

34
@Pavel Radzivilovsky: A proposito, i tuoi scritti su "Credo che alla fine moriranno tutte le altre codifiche. Ciò comporta che MS-Windows, Java, ICU, Python smettano di usarlo come il loro preferito." e "In particolare, penso che l'aggiunta di wchar_t al C ++ sia stata un errore, così come lo sono le aggiunte unicode al C ++ Ox." sono abbastanza ingenui o molto arroganti. E questo viene da qualcuno che sta programmando a casa con un Linux e che è contento dei caratteri UTF-8. Per dirla senza mezzi termini: non accadrà .
Paercebal,

157

I punti di codice Unicode non sono caratteri! A volte non sono nemmeno glifi (forme visive).

Qualche esempio:

  • Codici numerici romani come "ⅲ". (Un singolo personaggio che assomiglia a "iii".)
  • Personaggi accentati come "á", che possono essere rappresentati come un singolo carattere combinato "\ u00e1" o come carattere e segni diacritici separati "\ u0061 \ u0301".
  • Caratteri come il sigma minuscolo greco, che hanno forme diverse per le posizioni delle parole medio ("σ") e fine ("ς"), ma che dovrebbero essere considerati sinonimi per la ricerca.
  • Trattino discrezionale Unicode U + 00AD, che potrebbe essere visualizzato o meno visivamente, a seconda del contesto, e che viene ignorato per la ricerca semantica.

L'unico modo per ottenere correttamente l'editing Unicode è utilizzare una libreria scritta da un esperto o diventare un esperto e scriverne uno tu stesso. Se stai solo contando i punti di codice, stai vivendo in uno stato di peccato.


19
Questo. Molto questo UTF-16 può causare problemi, ma anche l'utilizzo di UTF-32 in tutto può (e lo farà) darti comunque problemi.
bcat

11
Cos'è un personaggio? È possibile definire un punto di codice come carattere e cavarsela praticamente bene. Se intendi un glifo visibile dall'utente, questo è qualcos'altro.
tchrist,

7
@tchrist è sicuro di allocare spazio quella definizione va bene, ma per qualcos'altro? Non così tanto. Se gestisci un personaggio che combina come unico personaggio (cioè per un'operazione di eliminazione o "prendi i primi N caratteri") otterrai un comportamento strano e sbagliato. Se un punto di codice ha significato solo se combinato con almeno un altro, non puoi gestirlo da solo in alcun modo ragionevole.
Voo,

6
@Pacerier, è tardi per la festa, ma devo commentare. Alcune lingue hanno insiemi molto grandi di potenziali combinazioni di segni diacritici (vedi vietnamita, cioè mệt đừ). Avere combinazioni piuttosto che un carattere per diacritico è molto utile.
asthasr,

21
una piccola nota sulla terminologia: codepoints non corrispondono ai caratteri Unicode ; ciò di cui Daniel parla qui sono personaggi percepiti dall'utente , che corrispondono a gruppi di grafismi unicode
Christoph

54

Esiste una semplice regola empirica su quale Unicode Transformation Form (UTF) utilizzare: - utf-8 per l'archiviazione e la comunicazione - utf-16 per l'elaborazione dei dati - potresti utilizzare utf-32 se la maggior parte dell'API della piattaforma che usi è utf-32 (comune nel mondo UNIX).

La maggior parte dei sistemi oggi utilizza utf-16 (Windows, Mac OS, Java, .NET, ICU, Qt). Vedi anche questo documento: http://unicode.org/notes/tn12/

Tornando a "UTF-16 come dannoso", direi: sicuramente no.

Le persone che hanno paura dei surrogati (pensando di trasformare Unicode in una codifica a lunghezza variabile) non comprendono le altre complessità (molto più grandi) che rendono molto complessa la mappatura tra caratteri e un codice Unicode: combinare caratteri, legature, selettori di variazioni , personaggi di controllo, ecc.

Basta leggere questa serie qui http://www.siao2.com/2009/06/29/9800913.aspx e vedere come UTF-16 diventa un problema facile.


26
Si prega di aggiungere alcuni esempi in cui UTF-32 è comune nel mondo UNIX!
maxschlepzig,

48
No, non si desidera utilizzare UTF-16 per l'elaborazione dei dati. È un dolore nel culo. Ha tutti gli svantaggi di UTF-8 ma nessuno dei suoi vantaggi. Sia UTF-8 che UTF-32 sono chiaramente superiori all'hack vizioso precedentemente noto come Mrs UTF-16, il cui nome da nubile era UCS-2.
tchrist,

34
Ieri ho appena trovato un bug nel equalsIgnoreCasemetodo della classe String core Java (anche altri nella classe string) che non sarebbe mai stato lì se Java avesse usato UTF-8 o UTF-32. Ci sono milioni di queste bombe dormienti in qualsiasi codice che utilizza UTF-16, e sono stufo di loro. UTF-16 è un vaiolo vizioso che affligge il nostro software con bug insidiosi per sempre. È chiaramente dannoso e dovrebbe essere deprecato e vietato.
tchrist,

7
@tchrist Wow, quindi una funzione consapevole non surrogata (perché è stata scritta quando non ce ne sono state ed è tristemente documentata in un modo che rende probabilmente impossibile adattarsi - specifica .toUpperCase (char)) si tradurrà in un comportamento sbagliato? Sei consapevole del fatto che una funzione UTF-32 con una mappa dei punti di codice obsoleta non la gestirà meglio? Inoltre l'intera API Java gestisce i surrogati non particolarmente bene e i punti più intricati su Unicode non lo sono affatto - e con il successivo la codifica utilizzata non avrebbe alcuna importanza.
Voo,

8
-1: Un incondizionato .Substring(1)in .NET è un banale esempio di qualcosa che interrompe il supporto per tutti gli Unicode non BMP. Tutto ciò che utilizza UTF-16 presenta questo problema; è troppo facile trattarlo come una codifica a larghezza fissa e vedi i problemi troppo raramente. Ciò lo rende una codifica attivamente dannosa se si desidera supportare Unicode.
Roman Starkov,

43

Si assolutamente.

Perché? Ha a che fare con l' esercizio del codice .

Se guardi queste statistiche sull'utilizzo dei punti di codice su un corpus di grandi dimensioni di Tom Christiansen vedrai che i punti di codice BMP trans-8 bit vengono utilizzati più ordini se la grandezza è maggiore dei codici non BMP:

 2663710 U+002013 ‹–›  GC=Pd    EN DASH
 1065594 U+0000A0 ‹ ›  GC=Zs    NO-BREAK SPACE
 1009762 U+0000B1 ‹±›  GC=Sm    PLUS-MINUS SIGN
  784139 U+002212 ‹−›  GC=Sm    MINUS SIGN
  602377 U+002003 ‹ ›  GC=Zs    EM SPACE

 544 U+01D49E ‹𝒞›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL C
 450 U+01D4AF ‹𝒯›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL T
 385 U+01D4AE ‹𝒮›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL S
 292 U+01D49F ‹𝒟›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL D
 285 U+01D4B3 ‹𝒳›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL X

Prendi il detto TDD: "Il codice non testato è un codice non funzionante" e riformulalo come "Il codice non esercitato è un codice non funzionante" e pensa a quanto spesso i programmatori devono gestire codici non BMP.

I bug relativi alla mancata gestione di UTF-16 come codifica a larghezza variabile hanno molte più probabilità di passare inosservati rispetto ai bug equivalenti in UTF-8 . Alcuni linguaggi di programmazione non garantiscono comunque di fornirti UTF-16 invece di UCS-2, e alcuni cosiddetti linguaggi di programmazione di alto livello offrono l'accesso a unità di codice anziché a punti di codice (anche C dovrebbe dare accesso a punti di codice se si utilizza wchar_t, indipendentemente da ciò che alcune piattaforme possono fare).


16
"I bug relativi alla mancata gestione di UTF-16 come codifica a larghezza variabile hanno molte più probabilità di passare inosservati rispetto ai bug equivalenti in UTF-8." Questo è il nocciolo del problema e quindi la risposta corretta.
Sean McMillan,

3
Precisamente. Se la tua gestione UTF-8 è bloccata, sarà immediatamente ovvio. Se la tua gestione UTF-8 è interrotta, noterai solo se inserisci caratteri Han o simboli matematici non comuni.
Lumaca meccanica

1
Molto vero, ma d'altra parte, a cosa servono i test unitari se dovresti dipendere dalla fortuna per trovare bug su casi meno frequenti?
musiphil,

@musiphil: quindi, quando è stata l'ultima volta che hai creato un test unitario per i personaggi non BMP?
ninjalj,

1
Per approfondire la mia precedente dichiarazione: anche con UTF-8, non si può essere certi di aver coperto tutti i casi dopo aver visto solo alcuni esempi funzionanti. Lo stesso con UTF-16: è necessario verificare se il codice funziona sia con i surrogati che con i surrogati. (Qualcuno potrebbe persino sostenere che UTF-8 ha almeno quattro casi principali mentre UTF-16 ne ha solo due.)
musiphil

40

Suggerirei che pensare che UTF-16 possa essere considerato dannoso dice che è necessario acquisire una maggiore comprensione dell'unicode .

Dato che sono stato sottoposto a downgrade per aver presentato la mia opinione su una domanda soggettiva, mi permetta di elaborare. Cosa ti disturba esattamente di UTF-16? Preferiresti se tutto fosse codificato in UTF-8? UTF-7? O che dire di UCS-4? Naturalmente alcune applicazioni non sono progettate per gestire il codice dei caratteri di sempre, ma sono necessarie, soprattutto nel dominio dell'informazione globale di oggi, per la comunicazione tra i confini internazionali.

Ma davvero, se ritieni che UTF-16 debba essere considerato dannoso perché confuso o può essere implementato in modo improprio (unicode certamente può essere), quale metodo di codifica dei caratteri sarebbe considerato non dannoso?

EDIT: Per chiarire: Perché considerare le implementazioni improprie di uno standard riflettono la qualità dello standard stesso? Come altri hanno successivamente notato, semplicemente perché un'applicazione utilizza uno strumento in modo inappropriato, non significa che lo strumento stesso sia difettoso. Se così fosse, potremmo probabilmente dire cose come "var keyword considerata dannosa" o "threading considerato dannoso". Penso che la domanda confonda la qualità e la natura dello standard con le difficoltà che molti programmatori hanno nell'implementarlo e nell'usarlo correttamente, cosa che provengo più dalla loro mancanza di comprensione del funzionamento dell'unicode, piuttosto che dall'unicode stesso.


33
-1: Che ne dici di affrontare alcune delle obiezioni di Artyom, piuttosto che limitarsi a patrocinarlo?

8
BTW: Quando ho iniziato a scrivere questo articolo, volevo quasi scrivere "L'articolo di Unicode su Softeare di Unicode dovrebbe essere considerato dannoso" perché ci sono molti errori. Ad esempio: la codifica utf-8 richiede fino a 4 caratteri e non 6. Inoltre, non distingue UCS-2 e UTF-16 che sono davvero diversi e che in realtà causano i problemi di cui parlo.

32
Inoltre, va notato che quando Joel scrisse quell'articolo, lo standard UTF-8 WAS 6 byte, non 4. RFC 3629 cambiò lo standard a 4 byte diversi mesi DOPO che scrisse l'articolo. Come la maggior parte di tutto su Internet, vale la pena leggere da più di una fonte ed essere consapevoli dell'età delle tue fonti. Il link non intendeva essere il "fine di tutti", ma piuttosto un punto di partenza.

7
Vorrei pic: utf-8 o utf-32 che sono: codifica a lunghezza variabile in quasi tutti i casi (incluso BMP) o codifica a lunghezza fissa sempre.

18
@iconiK: non essere sciocco. UTF-16 non è assolutamente lo standard di fatto per l'elaborazione del testo. Mostrami un linguaggio di programmazione più adatto all'elaborazione del testo che Perl, che ha sempre (beh, per più di un decennio) usato internamente caratteri astratti con una rappresentazione UTF-8 sottostante. Per questo motivo, ogni programma Perl gestisce automaticamente tutti gli Unicode senza che l'utente debba costantemente scansarsi con surrogati idioti. La lunghezza di una stringa è il suo conteggio in punti di codice, non in unità di codice. Qualsiasi altra cosa è pura stupidità che mette i retro in retrocompatibilità.
tchrist,

37

Non c'è nulla di sbagliato nella codifica Utf-16. Ma le lingue che trattano le unità a 16 bit come caratteri dovrebbero probabilmente essere considerate mal progettate. Avere un tipo chiamato ' char' che non rappresenta sempre un personaggio è piuttosto confuso. Poiché la maggior parte degli sviluppatori si aspetta che un tipo di carattere rappresenti un punto di codice o un carattere, molto probabilmente il codice si interromperà se esposto a caratteri oltre BMP.

Si noti tuttavia che anche l'uso di utf-32 non significa che ogni punto di codice a 32 bit rappresenterà sempre un carattere. A causa della combinazione di caratteri, un personaggio reale può essere costituito da diversi punti di codice. Unicode non è mai banale.

BTW. Esiste probabilmente la stessa classe di bug con piattaforme e applicazioni che prevedono caratteri a 8 bit, alimentati da Utf-8.


12
Nel caso di Java, se guardi la loro linea temporale ( java.com/en/javahistory/timeline.jsp ), vedi che lo sviluppo principale di String è avvenuto mentre Unicode era a 16 bit (è cambiato nel 1996). Dovevano sfruttare la capacità di gestire punti di codice non BMP, quindi la confusione.
Kathy Van Stone,

10
@Kathy: In realtà non è una scusa per C #. In generale, sono d'accordo, che dovrebbe esserci un CodePointtipo, che contiene un singolo punto di codice (21 bit), un CodeUnittipo, che contiene una singola unità di codice (16 bit per UTF-16) e un Charactertipo dovrebbe idealmente supportare un grapheme completo. Ma questo lo rende funzionalmente equivalente a un String...
Joey

1
Questa risposta ha quasi due anni, ma non posso fare a meno di commentarla. "Avere un tipo chiamato" char "che non rappresenta sempre un personaggio è piuttosto confuso." Eppure le persone lo usano sempre in C e simili per rappresentare dati interi che possono essere memorizzati in un singolo byte.
JAB

E ho visto un sacco di codice C che non gestisce correttamente la codifica dei caratteri.
dan04

1
C # ha una scusa diversa: è stato progettato per Windows e Windows è stato costruito su UCS-2 (è molto fastidioso che anche oggi le API di Windows non supportino UTF-8). Inoltre, penso che Microsoft volesse la compatibilità Java (.NET 1.0 aveva una libreria di compatibilità Java, ma hanno abbandonato il supporto Java molto rapidamente - Immagino che ciò sia dovuto alla causa di Sun contro la SM?)
Qwertie

20

La mia scelta personale è quella di utilizzare sempre UTF-8. È lo standard su Linux per quasi tutto. È retrocompatibile con molte app legacy. Esiste un sovraccarico minimo in termini di spazio extra utilizzato per i caratteri non latini rispetto agli altri formati UTF e un notevole risparmio di spazio per i caratteri latini. Sul web, le lingue latine regnano sovrane e penso che lo faranno per il prossimo futuro. E per affrontare uno dei principali argomenti del post originale: quasi tutti i programmatori sono consapevoli del fatto che UTF-8 a volte avrà caratteri multi-byte. Non tutti lo affrontano correttamente, ma di solito sono consapevoli, il che è più di quanto si possa dire per UTF-16. Ma, ovviamente, devi scegliere quello più appropriato per la tua applicazione. Ecco perché ce n'è più di uno in primo luogo.


3
UTF-16 è più semplice per qualsiasi cosa all'interno di BMP, ecco perché è usato così ampiamente. Ma sono anche un fan di UTF-8, inoltre non ha problemi con l'ordine dei byte, che funziona a suo vantaggio.
Malcolm,

2
Teoricamente si. In pratica ci sono cose come, diciamo, UTF-16BE, che significa UTF-16 in big endian senza BOM. Questa non è una cosa che ho inventato, questa è una codifica effettiva consentita nei tag ID3v2.4 (i tag ID3v2 fanno schifo, ma purtroppo sono ampiamente utilizzati). E in questi casi devi definire l'endianità esternamente, perché il testo stesso non contiene DBA. UTF-8 è sempre scritto in un modo e non ha questo problema.
Malcolm,

23
No, UTF-16 non è più semplice. È più difficile. Ti inganna e ti inganna pensando che sia una larghezza fissa. Tutto questo codice è rotto e tutto il moreso perché non si nota fino a quando non è troppo tardi. CASO IN PUNTO: Ieri ho appena trovato un altro stupido bug UTF-16 nelle librerie core Java, questa volta in String.equalsIgnoreCase, che è stato lasciato nel passeggino braindeath UCS-2, e quindi fallisce su 16/17 punti di codice Unicode validi. Da quanto tempo esiste quel codice? Nessuna scusa per essere buggy. UTF-16 porta a pura stupidità e un incidente in attesa di accadere. Corri urlando da UTF-16.
tchrist,

3
@tchrist One deve essere uno sviluppatore molto ignorante per non sapere che UTF-16 non ha una lunghezza fissa. Se inizi con Wikipedia, leggerai quanto segue in alto: "Produce un risultato a lunghezza variabile di una o due unità di codice a 16 bit per punto di codice". Le FAQ Unicode dicono lo stesso: unicode.org/faq//utf_bom.html#utf16-1 . Non so come UTF-16 possa ingannare qualcuno se è scritto ovunque che è di lunghezza variabile. Per quanto riguarda il metodo, non è mai stato progettato per UTF-16 e non dovrebbe essere considerato Unicode, così semplice.
Malcolm,

2
@tchrist Hai una fonte per le tue statistiche? Anche se i bravi programmatori sono scarsi, penso che sia un bene, perché diventiamo più preziosi. :) Per quanto riguarda le API Java, le parti basate sui caratteri potrebbero eventualmente diventare obsolete, ma ciò non garantisce che non verranno utilizzate. E sicuramente non verranno rimossi per motivi di compatibilità.
Malcolm,

18

Bene, esiste una codifica che utilizza simboli di dimensioni fisse. Intendo sicuramente UTF-32. Ma 4 byte per ogni simbolo sono troppo spazio sprecato, perché dovremmo usarlo nelle situazioni quotidiane?

A mio avviso, la maggior parte dei problemi appare dal fatto che alcuni software sono rimasti indietro rispetto allo standard Unicode, ma non sono stati pronti a correggere la situazione. Opera, Windows, Python, Qt - tutti apparivano prima che UTF-16 diventasse ampiamente noto o addirittura venisse alla luce. Posso confermare, tuttavia, che in Opera, Esplora risorse e Blocco note non ci sono più problemi con i caratteri al di fuori di BMP (almeno sul mio PC). Ma comunque, se i programmi non riconoscono le coppie surrogate, allora non usano UTF-16. Qualunque problema sorga dalla gestione di tali programmi, non hanno nulla a che fare con UTF-16 stesso.

Tuttavia, penso che i problemi del software legacy con solo il supporto BMP siano in qualche modo esagerati. I personaggi al di fuori di BMP si incontrano solo in casi e aree molto specifici. Secondo le FAQ ufficiali Unicode , "anche nel testo dell'Asia orientale, l'incidenza delle coppie surrogate dovrebbe essere in media inferiore all'1% di tutto l'archiviazione del testo". Naturalmente, i caratteri al di fuori di BMP non dovrebbero essere trascurati perché un programma non è conforme a Unicode altrimenti, ma la maggior parte dei programmi non è progettata per lavorare con testi contenenti tali caratteri. Ecco perché se non lo supportano, è spiacevole, ma non una catastrofe.

Ora consideriamo l'alternativa. Se UTF-16 non esistesse, non avremmo una codifica adatta per il testo non ASCII e tutto il software creato per UCS-2 dovrebbe essere completamente riprogettato per rimanere conforme Unicode. Quest'ultimo molto probabilmente rallenterebbe solo l'adozione di Unicode. Inoltre non saremmo stati in grado di mantenere la compatibilità con il testo in UCS-2 come UTF-8 rispetto ad ASCII.

Ora, mettendo da parte tutti i problemi legati all'eredità, quali sono gli argomenti contro la codifica stessa? Dubito davvero che gli sviluppatori al giorno d'oggi non sappiano che UTF-16 è di lunghezza variabile, è scritto ovunque a partire da Wikipedia. UTF-16 è molto meno difficile da analizzare rispetto a UTF-8, se qualcuno ha indicato la complessità come un possibile problema. Inoltre è sbagliato pensare che sia facile sbagliare nel determinare la lunghezza della stringa solo in UTF-16. Se usi UTF-8 o UTF-32, dovresti comunque essere consapevole che un punto di codice Unicode non significa necessariamente un carattere. A parte questo, non penso che ci sia qualcosa di sostanziale contro la codifica.

Pertanto non penso che la codifica stessa debba essere considerata dannosa. UTF-16 è un compromesso tra semplicità e compattezza e non vi è alcun danno nell'uso di ciò che è necessario dove è necessario . In alcuni casi devi rimanere compatibile con ASCII e hai bisogno di UTF-8, in alcuni casi vuoi lavorare con il lavoro con gli ideogrammi Han e conservare lo spazio usando UTF-16, in alcuni casi hai bisogno di rappresentazioni universali di caratteri per indicare un fisso- codifica della lunghezza. Usa ciò che è più appropriato, fallo e basta.


21
È una visione anglocentrica piuttosto ammiccante, Malcolm. Quasi alla pari di "ASCII è abbastanza buono per gli Stati Uniti - il resto del mondo dovrebbe adattarsi a noi".
Jonathan Leffler,

28
In realtà vengo dalla Russia e incontro continuamente i cirillici (compresi i miei programmi), quindi non credo di avere una visione anglo-centrica. :) Menzionare ASCII non è del tutto appropriato, perché non è Unicode e non supporta caratteri specifici. UTF-8, UTF-16, UTF-32 supportano gli stessi set di caratteri internazionali, sono destinati esclusivamente all'uso nelle loro aree specifiche. E questo è esattamente il mio punto: se usi principalmente l'inglese, usa UTF-8, se usi principalmente i cirillici, usa UTF-16, se usi lingue antiche, usa UTF-32. Abbastanza semplice.
Malcolm,

16
"Non è vero, anche gli script asiatici come giapponese, cinese o arabo appartengono a BMP. Lo stesso BMP è in realtà molto grande e certamente abbastanza grande da includere tutti gli script utilizzati al giorno d'oggi" Tutto è così sbagliato. BMP contiene 0xFFFF caratteri (65536). Il solo cinese ha più di questo. Gli standard cinesi (GB 18030) ne hanno di più. Unicode 5.1 ha già assegnato più di 100.000 caratteri.

12
@Marcolm: "Il BMP stesso è in realtà molto grande e certamente abbastanza grande da includere tutti gli script usati al giorno d'oggi" Non è vero. A questo punto Unicode ha già allocato circa 100K caratteri, molto più di quanto BMP possa accogliere. Ci sono grandi pezzi di caratteri cinesi fuori da BMP. E alcuni di essi sono richiesti da GB-18030 (standard cinese obbligatorio). Altri sono richiesti dagli standard (non obbligatori) giapponesi e coreani. Quindi, se provi a vendere qualcosa in quei mercati, hai bisogno di oltre il supporto BMP.

8
Tutto ciò che utilizza UTF-16 ma che può gestire solo caratteri BMP stretti non utilizza effettivamente UTF-16. È difettoso e rotto. La premessa dell'OP è solida: UTF-16 è dannoso, perché porta persone ingenue a scrivere codice non funzionante. O puoi gestire il testo Unicode o non puoi. Se non ci riesci, scegli un sottoinsieme, che è altrettanto stupido dell'elaborazione del testo solo ASCII.
tchrist,

16

Anni di lavoro sull'internazionalizzazione di Windows, specialmente nelle lingue dell'Asia orientale, potrebbero avermi corrotto, ma mi rivolgo a UTF-16 per le rappresentazioni di stringhe interne al programma e UTF-8 per l'archiviazione di file o di rete di documenti simili a testi in chiaro. UTF-16 di solito può essere elaborato più velocemente su Windows, quindi questo è il principale vantaggio dell'utilizzo di UTF-16 in Windows.

Fare il salto in UTF-16 ha migliorato notevolmente l'adeguatezza della media dei prodotti che gestiscono il testo internazionale. Ci sono solo alcuni casi ristretti in cui le coppie surrogate devono essere considerate (eliminazioni, inserzioni e interruzione di linea, in sostanza) e il caso medio è per lo più passante diretto. E a differenza delle codifiche precedenti come le varianti JIS, UTF-16 limita le coppie surrogate a un intervallo molto ristretto, quindi il controllo è molto rapido e funziona avanti e indietro.

Certo, è più o meno veloce anche in UTF-8 correttamente codificato. Ma ci sono anche molte applicazioni UTF-8 rotte che codificano erroneamente coppie surrogate come due sequenze UTF-8. Quindi UTF-8 non garantisce neanche la salvezza.

IE gestisce le coppie di surrogati ragionevolmente bene dal 2000 circa, anche se in genere le sta convertendo da pagine UTF-8 in una rappresentazione UTF-16 interna; Sono abbastanza sicuro che anche Firefox abbia capito bene, quindi non mi interessa davvero cosa fa Opera.

UTF-32 (aka UCS4) è inutile per la maggior parte delle applicazioni poiché è così esigente in termini di spazio, quindi è praticamente un non avviatore.


6
Non ho ancora ricevuto il tuo commento su UTF-8 e le coppie surrogate. Le coppie surrogate sono solo un concetto significativo nella codifica UTF-16, giusto? Forse il codice che converte direttamente dalla codifica UTF-16 alla codifica UTF-8 potrebbe sbagliare e, in tal caso, il problema sta leggendo erroneamente l'UTF-16, non scrivendo l'UTF-8. È giusto?
Craig McQueen,

11
Quello di cui Jason sta parlando è un software che implementa deliberatamente UTF-8 in quel modo: crea una coppia surrogata, quindi UTF-8 codifica ogni metà separatamente. Il nome corretto per quella codifica è CESU-8, ma Oracle (ad esempio) lo travisa come UTF-8. Java utilizza uno schema simile per la serializzazione degli oggetti, ma è chiaramente documentato come "UTF-8 modificato" e solo per uso interno. (Ora, se solo potessimo convincere le persone a LEGGERE quella documentazione e smettere di usare DataInputStream # readUTF () e DataOutputStream # writeUTF () in modo inappropriato ...)

AFAIK, UTF-32 è ancora codifica a lunghezza variabile e non è uguale a UCS4 che è un intervallo specifico di punti di codice.
Eonil,

@Eonil, UTF-32 sarà sempre distinguibile da UCS4 se abbiamo uno standard Unicode che presenta qualcosa come UCS5 o superiore.
JasonTrue,

@JasonTrue Tuttavia, solo i risultati sono uguali per coincidenza, non garantiti dal design. La stessa cosa è successa nell'indirizzamento della memoria a 32 bit, Y2K, UTF16 / UCS2. O abbiamo qualche garanzia di tale uguaglianza? Se lo abbiamo, lo userei volentieri. Ma non voglio scrivere un possibile codice fragile . Sto scrivendo un codice a livello di carattere e la mancanza di un modo garantito per transcodificare tra il punto di codice UTF <-> mi sta infastidendo molto.
Eonil,

16

UTF-8 è sicuramente la strada da percorrere, eventualmente accompagnata da UTF-32 per uso interno in algoritmi che richiedono un accesso casuale ad alte prestazioni (ma che ignora la combinazione di caratteri).

Sia UTF-16 che UTF-32 (così come le loro varianti LE / BE) soffrono di problemi di endianess, quindi non dovrebbero mai essere usati esternamente.


9
L'accesso casuale a tempo costante è possibile anche con UTF-8, basta usare le unità di codice anziché i punti di codice. Forse hai bisogno di un vero accesso casuale al punto di codice, ma non ho mai visto un caso d'uso, e invece è altrettanto probabile che tu voglia un accesso al cluster grapheme casuale.

15

UTF-16? decisamente dannoso. Solo il mio granello di sale qui, ma ci sono esattamente tre codifiche accettabili per il testo in un programma:

  • ASCII: quando si tratta di cose di basso livello (es. Microcontrollori) che non possono permettersi di meglio
  • UTF8: archiviazione in supporti a larghezza fissa come file
  • codici interi ("CP"?): un array dei numeri interi più grandi che sono convenienti per il tuo linguaggio di programmazione e la tua piattaforma (decade in ASCII nel limite delle risorse basse). Dovrebbe essere int32 su computer più vecchi e int64 su qualsiasi cosa con indirizzamento a 64 bit.

  • Ovviamente le interfacce per il codice legacy utilizzano la codifica necessaria per far funzionare correttamente il vecchio codice.


4
@simon buchan, il U+10ffffmassimo uscirà dalla finestra quando (non se) si esauriranno i punti di codice . Detto questo, usare int32 su un sistema p64 per la velocità è probabilmente sicuro, dal momento che dubito che supereranno U+ffffffffprima che tu sia costretto a riscrivere il tuo codice per i sistemi a 128 bit intorno al 2050. (Questo è il punto di "usare il più grande int che è conveniente "al contrario di" il più grande disponibile "(che probabilmente sarebbe int256 o bignums o qualcosa del genere).)
David X

1
@David: Unicode 5.2 codifica 107.361 punti di codice. Ci sono 867.169 punti di codice inutilizzati. "quando" è semplicemente stupido. Un punto di codice Unicode è definito come un numero compreso tra 0 e 0x10FFFF, una proprietà da cui UTF-16 dipende. (Anche il 2050 sembra abbassare di molto la stima per i sistemi a 128 bit quando un sistema a 64 bit può contenere l'intera Internet nel suo spazio degli indirizzi.)

3
@David: Il tuo "quando" si riferiva allo scadere dei punti di codice Unicode, non a uno switch a 128 bit che, sì, sarà nei prossimi secoli. A differenza della memoria, non c'è crescita esponenziale di personaggi, quindi il Consorzio Unicode ha specificamente garantito che non assegnerà mai un punto di codice sopra U+10FFFF. Questa è davvero una di quelle situazioni in cui 21 bit sono sufficienti per chiunque.

10
@Simon Buchan: almeno fino al primo contatto. :)

3
Unicode garantiva che non ci sarebbero stati punti di codice anche sopra U + FFFF.
Shannon Severance,

13

Unicode definisce i punti di codice fino a 0x10FFFF (1.114.112 codici), tutte le applicazioni in esecuzione in un ambiente multilingue che si occupa di stringhe / nomi di file ecc. Dovrebbero gestirlo correttamente.

Utf-16 : copre solo 1.112.064 codici. Sebbene quelli alla fine di Unicode provengano dagli aerei 15-16 (area di uso privato). Non può crescere ulteriormente in futuro se non rompendo il concetto di Utf-16 .

Utf-8 : copre teoricamente 2.216.757.376 codici. L'intervallo corrente di codici Unicode può essere rappresentato da una sequenza massima di 4 byte. Non ha problemi di ordine dei byte , è "compatibile" con ASCII.

Utf-32 : copre teoricamente 2 ^ 32 = 4.294.967.296 codici. Attualmente non è codificato a lunghezza variabile e probabilmente non lo sarà in futuro.

Questi fatti si spiegano da soli. Non capisco a favore dell'uso generale di Utf-16 . È codificato a lunghezza variabile (non è possibile accedervi tramite indice), ha problemi a coprire l'intero intervallo Unicode anche al momento, l'ordine dei byte deve essere gestito, ecc. Non vedo alcun vantaggio se non che è usato nativamente in Windows e in alcuni altri posti. Anche se durante la scrittura di codice multipiattaforma è probabilmente meglio usare Utf-8 in modo nativo e fare conversioni solo agli end point in modo dipendente dalla piattaforma (come già suggerito). Quando è necessario l'accesso diretto tramite indice e la memoria non è un problema, è necessario utilizzare Utf-32 .

Il problema principale è che molti programmatori che si occupano di Windows Unicode = Utf-16 non sanno nemmeno o ignorano il fatto che è codificato a lunghezza variabile.

Il modo in cui è normalmente nella piattaforma * nix è abbastanza buono, le stringhe c (char *) interpretate come codificate Utf-8 , le stringhe c larghe (wchar_t *) interpretate come Utf-32 .


7
Nota: UTF-16 copre tutto Unicode poiché il consorzio Unicode ha deciso che 10FFFF è la gamma TOP di Unicode e ha definito UTF-8 massima lunghezza di 4 byte e l'intervallo esplicitamente escluso 0xD800-0xDFFF dalla gamma di punti di codice validi e questo intervallo viene utilizzato per la creazione di coppie surrogate. Pertanto, qualsiasi testo Unicode valido può essere rappresentato con ciascuna di una di queste codifiche. Anche sulla crescita verso il futuro. Non sembra che 1 milione di punti di codice non sarebbero sufficienti in un futuro lontano.

7
@Kerrek: errato: UCS-2 non è una codifica Unicode valida. Tutte le codifiche UTF- * per definizione possono rappresentare qualsiasi punto di codice Unicode che è legale per lo scambio. UCS-2 può rappresentare molto meno di questo, oltre a pochi altri. Ripeti: UCS-2 non è una codifica Unicode valida, qualsiasi più di quanto ASCII lo sia.
tchrist,

1
"Non capisco a favore dell'uso generale di Utf-8 . È codificato a lunghezza variabile (non accessibile dall'indice)"
Ian Boyd,

9
@Ian Boyd, la necessità di accedere al carattere individuale di una stringa in un modello di accesso casuale è incredibilmente sopravvalutata. È comune come voler calcolare la diagonale di una matrice di personaggi, il che è super raro. Le stringhe vengono virtualmente sempre elaborate in sequenza e poiché l'accesso al carattere UTF-8 N + 1 dato che ci si trova nel carattere UTF-8 N è O (1), non vi è alcun problema. C'è sorprendentemente poco bisogno di accedere casualmente alle stringhe. Se pensi che valga la pena di andare allo spazio di archiviazione su UTF-32 anziché UTF-8 è la tua opinione, ma per me è del tutto un problema.
tchrist,

2
@tchrist, ti garantirò che le stringhe sono praticamente sempre elaborate in sequenza se includi l'iterazione inversa come "sequenziale" e allunghi un po 'di più il confronto tra l'estremità finale di una stringa e una stringa nota. Due scenari molto comuni sono il troncamento degli spazi bianchi dalla fine delle stringhe e il controllo dell'estensione del file alla fine di un percorso.
Andy Dent,

11

Aggiungi questo alla lista:

Lo scenario presentato è semplice (anche più semplice di come lo presenterò qui di quanto non fosse in origine!): 1.A WinForms TextBox si trova su un modulo, vuoto. Ha una lunghezza massima impostata su 20 .

2.L'utente digita nella TextBox o forse incolla il testo in essa.

3.Non importa che cosa digiti o incolli nella TextBox, sei limitato a 20, anche se emetterà simpaticamente un segnale di testo oltre i 20 (YMMV qui; ho cambiato il mio schema sonoro per darmi quell'effetto!).

4. Il piccolo pacchetto di testo viene quindi inviato da qualche altra parte, per iniziare un'avventura emozionante.

Questo è uno scenario semplice e chiunque può scriverlo nel tempo libero. L'ho scritto da solo in più linguaggi di programmazione usando WinForms, perché ero annoiato e non l'avevo mai provato prima. E con il testo in più lingue effettive perché sono cablato in quel modo e ho più layout di tastiera di chiunque altro nell'intero universo.

Ho anche chiamato la forma Magic Carpet Ride , per aiutare a migliorare la noia.

Questo non ha funzionato, per quello che vale.

Quindi, invece, ho inserito i seguenti 20 caratteri nel mio modulo Magic Carpet Ride :

0123401234012340123 𠀀

Uh Oh.

Quest'ultimo personaggio è U + 20000, il primo ideografo Extension B di Unicode (aka U + d840 U + dc00, per i suoi amici intimi di cui non si vergogna di essere travestito, per così dire, di fronte) ....

inserisci qui la descrizione dell'immagine

E ora abbiamo una partita a baseball.

Perché quando parla TextBox.MaxLength

Ottiene o imposta il numero massimo di caratteri che è possibile immettere manualmente nella casella di testo.

ciò che significa veramente è

Ottiene o imposta il numero massimo di unità di codice LE UTF-16 che possono essere inserite manualmente nella casella di testo e troncerà senza pietà la schifezza vivente di qualsiasi stringa che tenta di giocare a giochi carini con l'idea di carattere linguistico che solo qualcuno ossessionato come quel compagno Kaplan troverà offensivo (accidenti ha bisogno di uscire di più!).

Proverò a vedere come aggiornare il documento ....
I lettori abituali che ricordano le mie serie da UCS-2 a UTF-16 noteranno la mia infelicità con la nozione semplicistica di TextBox.MaxLength e come dovrebbe gestire almeno questo caso dove il suo comportamento draconiano crea una sequenza illegale, quella che altre parti del Framework .Net possono lanciare a

  • System.Text.EncoderFallbackException: impossibile tradurre il carattere Unicode \ uD850 all'indice 0 nella tabella codici specificata. *

eccezione se si passa questa stringa altrove nel .Net Framework (come stava facendo il mio collega Dan Thompson).

Ora va bene, forse l'intera serie da UCS-2 a UTF-16 è fuori dalla portata di molti.
Ma non è ragionevole aspettarsi che TextBox.Text non produca System.Stringche non provocherà il lancio di un altro pezzo di .Net Framework? Voglio dire, non è che ci sia una possibilità sotto forma di un evento sul controllo che ti dice dell'imminente troncamento in cui puoi facilmente aggiungere la convalida più intelligente - convalida che il controllo stesso non si preoccupa di fare. Vorrei arrivare al punto di dire che questo controllo punk sta infrangendo un contratto di sicurezza che potrebbe anche portare a problemi di sicurezza se si può classificare causando eccezioni impreviste per terminare un'applicazione come una sorta di negazione del servizio. Perché qualsiasi processo o metodo o algoritmo o tecnica WinForms dovrebbe produrre risultati non validi?

Fonte: Blog su Michael S. Kaplan MSDN


Grazie, ottimo collegamento! L'ho aggiunto all'elenco dei problemi nella domanda.

9

Non direi necessariamente che UTF-16 è dannoso. Non è elegante, ma serve al suo scopo di retrocompatibilità con UCS-2, proprio come GB18030 fa con GB2312 e UTF-8 fa con ASCII.

Ma apportare una modifica fondamentale alla struttura di Unicode a metà flusso, dopo che Microsoft e Sun avevano creato enormi API attorno a caratteri a 16 bit, era dannoso. L'incapacità di diffondere la consapevolezza del cambiamento era più dannosa.


8
UTF-8 è un superset di ASCII, ma UTF-16 NON è un superset di UCS-2. Sebbene quasi un superset, una corretta codifica di UCS-2 in UTF-8 provoca l'abominio noto come CESU-8; UCS-2 non ha surrogati, ma solo punti di codice ordinari, quindi devono essere tradotti come tali. Il vero vantaggio di UTF-16 è che è più semplice aggiornare una base di codice UCS-2 rispetto a una riscrittura completa per UTF-8. Divertente, eh?

1
Certo, tecnicamente UTF-16 non è un superset di UCS-2, ma quando mai U + D800 a U + DFFF sono mai stati usati per qualcosa tranne i surrogati UTF-16?
dan04,

2
Non importa Qualsiasi elaborazione diversa dal passaggio cieco attraverso il bytestream richiede la decodifica delle coppie surrogate, cosa che non si può fare se la si sta trattando come UCS-2.

6

UTF-16 è il miglior compromesso tra gestione e spazio ed è per questo che la maggior parte delle piattaforme principali (Win32, Java, .NET) lo usano per la rappresentazione interna delle stringhe.


31
-1 perché è probabile che UTF-8 sia più piccolo o non significativamente diverso. Per alcuni script asiatici UTF-8 è di tre byte per glifo mentre UTF-16 è solo due, ma questo è bilanciato dal fatto che UTF-8 è solo un byte per ASCII (che appare spesso anche all'interno delle lingue asiatiche in nomi di prodotti, comandi e simili cose). Inoltre, nelle suddette lingue, un glifo trasmette più informazioni di un carattere latino, quindi è giustificato che occupi più spazio.

32
Non definirei la combinazione dei lati peggiori di entrambe le opzioni come un buon compromesso.

18
Non è più facile di UTF-8. È anche di lunghezza variabile.
luiscubal

36
Lasciando da parte i dibattiti sui vantaggi di UTF-16: Quello che hai citato non è il motivo per Windows, Java o .NET usando UTF-16. Windows e Java risalgono a un'epoca in cui Unicode era una codifica a 16 bit. UCS-2 era una scelta ragionevole allora. Quando Unicode divenne una codifica a 21 bit, la migrazione a UTF-16 era la scelta migliore che esistessero le piattaforme. Ciò non aveva nulla a che fare con la facilità di gestione o compromessi nello spazio. È solo una questione di eredità.
Joey,

10
.NET eredita qui l'eredità di Windows.
Joey,

6

Non ho mai capito il punto di UTF-16. Se si desidera la rappresentazione più efficiente in termini di spazio, utilizzare UTF-8. Se vuoi essere in grado di trattare il testo come lunghezza fissa, usa UTF-32. Se non si desidera nessuno dei due, utilizzare UTF-16. Peggio ancora, dal momento che tutti i caratteri comuni (piano multilingue di base) in UTF-16 rientrano in un singolo punto di codice, i bug che presuppongono che UTF-16 sia di lunghezza fissa saranno sottili e difficili da trovare, mentre se si tenta di farlo questo con UTF-8, il tuo codice fallirà velocemente e ad alta voce non appena proverai a internazionalizzare.


6

Dal momento che non posso ancora commentare, inserisco questo come una risposta, dal momento che sembra che non potrei altrimenti contattare gli autori di utf8everywhere.org. È un peccato che non ottengo automaticamente il privilegio di commento, poiché ho abbastanza reputazione su altri cambi di stack.

Ciò si intende come un commento al parere: Sì, UTF-16 dovrebbe essere considerata una risposta dannosa .

Una piccola correzione:

Per evitare che uno passi accidentalmente un UTF-8 char*nelle versioni di stringa ANSI delle funzioni dell'API di Windows, si dovrebbe definire UNICODE, no _UNICODE. _UNICODEfunzioni come mappe _tcslenper wcslen, non MessageBoxa MessageBoxW. Invece, il UNICODEdefine si occupa di quest'ultimo. Per prova, questo è dall'intestazione di MS Visual Studio 2005 WinUser.h:

#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

Come minimo, questo errore dovrebbe essere corretto utf8everywhere.org.

Un consiglio:

Forse la guida dovrebbe contenere un esempio di uso esplicito della versione a stringa larga di una struttura di dati, per rendere meno facile perdere / dimenticare. L'uso di versioni di stringhe di strutture dati oltre all'uso di versioni di stringhe di funzioni rende ancora meno probabile che si chiami accidentalmente una versione di stringa ANSI di tale funzione.

Esempio dell'esempio:

WIN32_FIND_DATAW data; // Note the W at the end.
HANDLE hSearch = FindFirstFileW(widen("*.txt").c_str(), &data);
if (hSearch != INVALID_HANDLE_VALUE)
{
    FindClose(hSearch);
    MessageBoxW(nullptr, data.cFileName, nullptr, MB_OK);
}

Concordato; Grazie! Aggiorneremo il documento. Il documento necessita ancora di più sviluppo e aggiunta di informazioni sui database. Siamo felici di ricevere contributi di formulazioni.
Pavel Radzivilovsky,

@PavelRadzivilovsky _UNICODEè ancora lì :(
cubuspl42

grazie per averlo ricordato. cubus, Jelle, Vuoi un utente al nostro SVN?
Pavel Radzivilovsky,

@Pavel Certo, lo apprezzerei!
Jelle Geerts,

@JelleGeerts: mi scuso per questo ritardo. Puoi sempre contattarci tramite le nostre e-mail (collegate dal manifesto) o Facebook. Siamo facili da trovare. Anche se credo che abbiamo risolto il problema che hai portato qui (e ti ho accreditato lì), tutti i dibattiti UTF-8 vs UTF-16 sono ancora rilevanti. Se hai altro da contribuire, sentiti libero di contattarci attraverso questi canali privati.
ybungalobill,

5

Qualcuno ha detto che UCS4 e UTF-32 erano uguali. No, ma so cosa intendi. Uno di questi è una codifica dell'altro, però. Vorrei che avessero pensato di specificare l'endianità dal primo in modo che non avremmo combattuto la battaglia dell'endianità anche qui. Non avrebbero potuto vederlo arrivare? Almeno UTF-8 è lo stesso ovunque (a meno che qualcuno non stia seguendo le specifiche originali con 6 byte).

Se si utilizza UTF-16 si deve includere la gestione per i caratteri multibyte. Non puoi andare all'ennesimo carattere indicizzando 2N in un array di byte. Devi percorrerlo o avere indici di caratteri. Altrimenti hai scritto un bug.

L'attuale bozza delle specifiche di C ++ afferma che UTF-32 e UTF-16 possono avere varianti little-endian, big-endian e non specificate. Veramente? Se Unicode avesse specificato che tutti dovevano fare little-endian sin dall'inizio, sarebbe stato tutto più semplice. (Sarei andato bene anche con il big-endian.) Invece, alcune persone l'hanno implementato in un modo, un altro e ora siamo bloccati con la stupidità per niente. A volte è imbarazzante essere un ingegnere del software.


L'endianità non specificata dovrebbe includere la DBA come primo carattere, usata per determinare in che modo leggere la stringa. UCS-4 e UTF-32 sono effettivamente gli stessi al giorno d'oggi, cioè un valore numerico UCS tra 0 e 0x10FFFF memorizzato in un numero intero a 32 bit.

5
@Tronic: tecnicamente, questo non è vero. Sebbene UCS-4 sia in grado di memorizzare qualsiasi numero intero a 32 bit, UTF-32 non può memorizzare i punti di codice non di carattere che sono illegali per lo scambio, come 0xFFFF, 0xFFFE e tutti i surrogati. UTF è una codifica di trasporto, non interna.
tchrist

I problemi di endianness sono inevitabili fino a quando processori diversi continuano a utilizzare ordini di byte diversi. Tuttavia, sarebbe stato bello se ci fosse un ordine di byte "preferito" per l'archiviazione dei file di UTF-16.
Qwertie

Anche se UTF-32 è a larghezza fissa per punti di codice , non è a larghezza fissa per caratteri . (Hai sentito di qualcosa chiamato "combinare personaggi"?) Quindi non puoi andare all'ennesimo carattere semplicemente indicizzando 4N nella matrice di byte.
musiphil,

2

Non penso che sia dannoso se lo sviluppatore è abbastanza attento.
E dovrebbero accettare questo compromesso se lo sanno anche bene.

Come sviluppatore di software giapponese, trovo UCS-2 abbastanza grande e limitare lo spazio apparentemente semplifica la logica e riduce la memoria di runtime, quindi usare utf-16 sotto la limitazione di UCS-2 è abbastanza buono.

Esistono file system o altre applicazioni che presuppongono che i punti di codice e i byte siano proporzionali, in modo che il numero di punti di codice non elaborati possa essere adattato a un archivio di dimensioni fisse.

Un esempio è NTFS e VFAT che specifica UCS-2 come codifica di archiviazione del nome file.

Se quell'esempio vuole davvero estendersi per supportare UCS-4, potrei essere d'accordo usando utf-8 per tutto comunque, ma la lunghezza fissa ha buoni punti come:

  1. può garantire la dimensione in base alla lunghezza (la dimensione dei dati e la lunghezza del punto di codice sono proporzionali)
  2. può utilizzare il numero di codifica per la ricerca hash
  3. i dati non compressi sono di dimensioni ragionevoli (rispetto a utf-32 / UCS-4)

In futuro, quando la potenza di memoria / elaborazione sarà economica anche in tutti i dispositivi incorporati, potremmo accettare che il dispositivo sia un po 'lento per mancamenti di cache aggiuntivi o guasti di pagina e utilizzo di memoria extra, ma questo non accadrà nel prossimo futuro immagino ...


3
Per coloro che leggono questo commento, vale la pena notare che UCS-2 non è la stessa cosa di UTF-16. Cerca le differenze per capire.
mikebabcock,

1

"Una delle codifiche più popolari, UTF-16, dovrebbe essere considerata dannosa?"

Molto probabilmente, ma le alternative non dovrebbero necessariamente essere considerate molto migliori.

Il problema fondamentale è che ci sono molti concetti diversi su: glifi, caratteri, punti di codice e sequenze di byte. Il mapping tra ciascuno di questi è non banale, anche con l'aiuto di una libreria di normalizzazione. (Ad esempio, alcuni caratteri in lingue europee che sono scritti con una scrittura latina non sono scritti con un singolo punto di codice Unicode. E questo è alla fine più semplice della complessità!) Ciò che significa che ottenere tutto corretto è abbastanza sorprendente difficile; ci si aspettano bizzarri bug (e invece di lamentarsi qui, informa i manutentori del software in questione).

L'unico modo in cui UTF-16 può essere considerato dannoso al contrario, diciamo, UTF-8 è che ha un modo diverso di codificare punti di codice al di fuori del BMP (come una coppia di surrogati). Se il codice desidera accedere o scorrere per punto di codice, significa che deve essere consapevole della differenza. OTOH, significa che un corpus sostanziale di codice esistente che assume "caratteri" può sempre adattarsi a una quantità di due byte - un presupposto abbastanza comune, se sbagliato, può almeno continuare a funzionare senza ricostruire tutto. In altre parole, almeno riesci a vedere quei personaggi che non vengono gestiti bene!

Vorrei rovesciare la tua domanda e dire che l'intero maledetto shebang di Unicode dovrebbe essere considerato dannoso e tutti dovrebbero usare una codifica a 8 bit, tranne che ho visto (negli ultimi 20 anni) dove questo porta: orribile confusione sulle varie codifiche ISO 8859, oltre all'intero set di quelle utilizzate per il cirillico, e la suite EBCDIC, e ... beh, Unicode per tutti i suoi difetti batte questo. Se non fosse stato un brutto compromesso tra incomprensioni di diversi paesi.


Conoscendo la nostra fortuna, tra qualche anno ci ritroveremo a corto di spazio in UTF-16. Meh.
Donal Fellows,

3
Il problema fondamentale è che il testo è ingannevolmente difficile. Nessun approccio alla rappresentazione di tali informazioni in modo digitale può essere semplice. È la stessa ragione per cui le date sono difficili, i calendari sono duri, il tempo è difficile, i nomi personali sono difficili, gli indirizzi postali sono difficili: ogni volta che le macchine digitali si intersecano con costrutti culturali umani, la complessità esplode. È un dato di fatto. Gli esseri umani non funzionano sulla logica digitale.
Aristotele Pagaltzis,
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.