Le format UTF-16 doit-il être considéré comme dangereux?


432

Je vais demander quelle est probablement une question assez controversée: "Un des encodages les plus populaires, UTF-16, devrait-il être considéré comme dangereux?"

Pourquoi je pose cette question?

Combien de programmeurs sont conscients du fait qu'UTF-16 est en réalité un encodage à longueur variable? J'entends par là qu'il existe des points de code qui, représentés par des paires de substitution, prennent plus d'un élément.

Je connais; De nombreuses applications, infrastructures et API utilisent UTF-16, telles que la chaîne Java, la chaîne C #, les API Win32, les bibliothèques d'interface graphique Qt, la bibliothèque ICU Unicode, etc. Cependant, le traitement comporte de nombreux bogues de caractères hors BMP (caractères qui devraient être codés en utilisant deux éléments UTF-16).

Par exemple, essayez d’éditer l’un de ces caractères:

  • 𝄞 ( U + 1D11E ) SYMBOLE MUSICAL G CLEF
  • 𝕥 ( U + 1D565 ) MINUSCULE MATHÉMATIQUE AJOURÉE T
  • 𝟶 ( U + 1D7F6 ) CHIFFRE MATHÉMATIQUE À MONOSPACE ZÉRO
  • 𠂊 ( U + 2008A ) Caractère Han

Il se peut que vous en manquiez, en fonction des polices que vous avez installées. Ces caractères sont tous en dehors du plan BMP (Basic Multilingual Plane). Si vous ne pouvez pas voir ces caractères, vous pouvez également essayer de les regarder dans la référence de caractère Unicode .

Par exemple, essayez de créer des noms de fichiers sous Windows contenant ces caractères. essayez de supprimer ces caractères avec un "retour arrière" pour voir comment ils se comportent dans différentes applications qui utilisent UTF-16. J'ai fait des tests et les résultats sont assez mauvais:

  • Opera a des problèmes pour les éditer (effacez les 2 appuis nécessaires sur le retour arrière)
  • Le Bloc-notes ne peut pas les traiter correctement (supprimez les 2 appuis requis sur le retour arrière).
  • La modification des noms de fichier dans les boîtes de dialogue de la fenêtre est interrompue (supprimer 2 appuis requis sur le retour arrière)
  • Toutes les applications QT3 ne peuvent pas les gérer - affichez deux carrés vides au lieu d'un symbole.
  • Python n'encode pas correctement ces caractères lorsqu'il est utilisé directement u'X'!=unicode('X','utf-16')sur certaines plates-formes lorsque X est un caractère extérieur à BMP.
  • Python 2.5 unicodedata ne parvient pas à obtenir les propriétés de tels caractères lorsque python est compilé avec des chaînes Unicode UTF-16.
  • StackOverflow semble supprimer ces caractères du texte s’il est directement modifié en tant que caractères Unicode (ces caractères sont affichés à l’aide d’échappements HTML Unicode).
  • WinForms TextBox peut générer une chaîne non valide lorsqu'il est limité avec MaxLength.

Il semble que ces bogues soient extrêmement faciles à trouver dans de nombreuses applications utilisant UTF-16.

Alors ... Pensez-vous que l'UTF-16 devrait être considéré comme dangereux?


64
Pas vraiment correct. J'explique, si vous écrivez "שָׁ" le caractère composé qui se compose de "", "" et "", vovels, puis la suppression de chacun d'eux est logique, vous supprimez un point de code lorsque vous appuyez sur " backspace "et supprimez tous les caractères, y compris les vovels, lorsque vous appuyez sur" del ". Mais vous ne produisez jamais d’ état illégal de texte - des points de code illégaux. Par conséquent, la situation lorsque vous appuyez sur la touche retour arrière et obtenez du texte illégat est incorrecte.

41
CiscoIPPhone: Si un bogue "est signalé plusieurs fois, par plusieurs personnes différentes", quelques années plus tard, un développeur écrit sur un blog de développement: "Croyez-le ou non, le comportement est principalement intentionnel!", Puis (pour j'ai tendance à penser que ce n'est probablement pas la meilleure décision de conception jamais prise. :-) Ce n'est pas parce que c'est intentionnel que ce n'est pas un bug.

145
Très bonne publication. UTF-16 est en effet le "pire des deux mondes": UTF8 est de longueur variable, couvre tout Unicode, nécessite un algorithme de transformation vers et à partir de points de code bruts, se limite à ASCII et ne présente aucun problème d'endianisme. UTF32 est de longueur fixe, ne nécessite aucune transformation, mais prend plus de place et pose des problèmes d’endianisme. Jusqu'ici tout va bien, vous pouvez utiliser UTF32 en interne et UTF8 pour la sérialisation. Mais UTF16 ne présente aucun avantage: il dépend de l’endian, il est de longueur variable, il prend beaucoup de place, il n’est pas compatible ASCII. Les efforts nécessaires pour gérer correctement UTF16 pourraient être mieux dépensés sur UTF8.
Kerrek SB le

26
@Ian: UTF-8 N'A PAS les mêmes réserves que UTF-8. Vous ne pouvez pas avoir de substituts dans UTF-8. UTF-8 ne se fait pas passer pour quelque chose qu'il n'est pas, mais la plupart des programmeurs utilisant UTF-16 l'utilisent mal. Je connais. Je les ai regardés encore et encore et encore et encore.
tchrist

18
De plus, UTF-8 n'a pas le problème car tout le monde le traite comme un encodage à largeur variable. La raison pour laquelle UTF-16 a ce problème est que tout le monde le traite comme un encodage à largeur fixe.
Christoffer Hammarström

Réponses:


340

C'est une vieille réponse.
Voir UTF-8 Everywhere pour les dernières mises à jour.

Opinion: Oui, UTF-16 devrait être considéré comme nuisible . La raison même de son existence est qu’il ya quelque temps, on pensait à tort que widechar allait devenir ce que l’UCS-4 est maintenant.

Malgré "l'anglo-centrisme" d'UTF-8, il convient de le considérer comme le seul encodage utile pour le texte. On peut faire valoir que les codes sources des programmes, les pages Web et les fichiers XML, les noms de fichiers du système d'exploitation et les autres interfaces de texte d'ordinateur à ordinateur n'auraient jamais existé. Mais quand ils le font, le texte n'est pas seulement pour les lecteurs humains.

D'autre part, les frais généraux UTF-8 sont un faible prix à payer alors qu'ils présentent des avantages importants. Des avantages tels que la compatibilité avec du code non au courant qui passe simplement des chaînes avec char*. C'est une bonne chose. Il y a peu de caractères utiles qui sont plus courts dans UTF-16 que dans UTF-8.

Je crois que tous les autres encodages vont finir par mourir. Cela implique que MS-Windows, Java, ICU et Python cessent de l'utiliser comme leur favori. Après de longues recherches et discussions, les conventions de développement de mon entreprise interdisent l’utilisation de UTF-16 partout, à l’exception des appels API de système d’exploitation, et ce, malgré l’importance des performances de nos applications et le fait que nous utilisons Windows. Les fonctions de conversion ont été développées pour convertir les UTF8 toujours supposés std::stringen UTF-16 natif, que Windows ne prend pas correctement en charge .

Aux personnes qui disent " utilisez ce qui est nécessaire où cela est nécessaire ", je dis: il est extrêmement avantageux d'utiliser le même encodage partout, et je ne vois aucune raison suffisante pour faire autrement. En particulier, je pense que l'ajout wchar_tau C ++ était une erreur, de même que les ajouts Unicode à C ++ 0x. Ce qui doit cependant être exigé des implémentations STL, c’est que chaque paramètre std::stringou char*paramètre serait considéré comme compatible avec unicode.

Je suis également contre l’ approche « utilise ce que tu veux ». Je ne vois aucune raison pour une telle liberté. Il y a suffisamment de confusion au sujet du texte, ce qui entraîne tout ce logiciel cassé. Cela dit, je suis convaincu que les programmeurs doivent enfin parvenir à un consensus sur le format UTF-8. (Je viens d'un pays qui ne parle pas l'asci et j'ai grandi sous Windows. On m'attend donc à ce que je m'attaque pour la dernière fois à UTF-16 pour des raisons religieuses).

J'aimerais partager davantage d'informations sur la manière dont je rédige du texte sous Windows et sur ce que je recommande à tout le monde pour l'exactitude vérifiée au moment de la compilation, la facilité d'utilisation et une meilleure multiplicité du code. La suggestion diffère substantiellement de ce qui est généralement recommandé comme la bonne façon d'utiliser Unicode sur Windows. Pourtant, une recherche approfondie de ces recommandations a abouti à la même conclusion. Alors, voici:

  • N'utilisez pas wchar_tou std::wstringdans aucun endroit autre que le point adjacent aux API acceptant UTF-16.
  • Ne pas utiliser _T("")ou L""UTF-16 littéraux (OMI Ceux - ci devraient être retirés de la norme, comme une partie de deprecation UTF-16).
  • N'utilisez pas de types, fonctions ou leurs dérivés sensibles à la _UNICODEconstante, tels que LPTSTRou CreateWindow().
  • Pourtant, _UNICODEtoujours défini, pour éviter de passer des char*chaînes à WinAPI compilées en silence
  • std::stringset char*n'importe où dans le programme sont considérés comme UTF-8 (sauf indication contraire)
  • Toutes mes chaînes sont std::string, bien que vous puissiez passer char * ou chaîne littérale à convert(const std::string &).
  • utilisez uniquement les fonctions Win32 qui acceptent widechars ( LPWSTR). Jamais ceux qui acceptent LPTSTRou LPSTR. Passer les paramètres de cette façon:

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

    (La stratégie utilise les fonctions de conversion ci-dessous.)

  • Avec les chaînes 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);
    
  • Utilisation de fichiers, noms de fichiers et fstream sous Windows:

    • Ne jamais transmettre d' argument std::stringou d' const char*argument de nom de fichier à la fstreamfamille. MSVC STL ne prend pas en charge les arguments UTF-8, mais possède une extension non standard qui doit être utilisée comme suit:
    • Convertir les std::stringarguments en std::wstringavec Utils::Convert:

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

      Nous devrons supprimer manuellement le convertisseur, lorsque l'attitude de MSVC vis-à-vis des fstreamchangements.

    • Ce code n'est pas multiplateforme et devra peut-être être modifié manuellement à l'avenir
    • Voir le fstreamcas de recherche / discussion 4215 unicode pour plus d'informations.
    • Ne jamais produire de fichiers de sortie texte avec un contenu non UTF8
    • Évitez d'utiliser fopen()pour des raisons RAII / OOD. Si nécessaire, utilisez _wfopen()et les conventions WinAPI ci-dessus.

// 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
Je ne suis pas d'accord Les avantages de utf16 par rapport à utf8 pour de nombreuses langues asiatiques dominent complètement vos arguments. Il est naïf d’espérer que les Japonais, les Thaïlandais, les Chinois, etc. abandonnent cet encodage. Les conflits problématiques entre les jeux de caractères surviennent lorsque ceux-ci semblent généralement similaires, à l'exception des différences. Je suggère de standardiser sur: 7bit fixe: iso-irv-170; Variable 8 bits: utf8; Variable 16 bits: utf16; 32bits corrigés: ucs4.

82
@ Charles: merci pour votre contribution. Certes, certains caractères BMP sont plus longs dans UTF-8 que dans UTF-16. Mais regardons les choses en face: le problème ne réside pas dans les octets que prennent les caractères chinois BMP, mais dans la complexité de la conception logicielle. De toute façon, si un programmeur chinois doit concevoir des caractères de longueur variable, il semble que UTF-8 reste un petit prix à payer comparé aux autres variables du système. Il pourrait utiliser UTF-16 comme algorithme de compression si l'espace est tellement important, mais même dans ce cas, il n'y aura aucune correspondance pour LZ. Après la compression LZ ou une autre compression générique, la taille et l'entropie sont identiques.

32
Ce que je dis en gros, c’est que la simplification offerte par le codage One qui est également compatible avec les programmes char * existants, et qui est aussi la plus populaire aujourd’hui, est inimaginable. C'est presque comme dans les bons vieux jours en "texte clair". Voulez-vous ouvrir un fichier avec un nom? Pas besoin de vous soucier du type d'unicode que vous faites, etc. Je suggère que nous, les développeurs, limitions l'UTF-16 à des cas très particuliers d'optimisation sévère où une infime performance vaut la peine de travailler-mois.

17
Linux a eu une exigence spécifique en choisissant d’utiliser UTF-8 en interne: la compatibilité avec Unix. Windows n'en avait pas besoin, et donc lorsque les développeurs ont implémenté Unicode, ils ont ajouté les versions UCS-2 de presque toutes les fonctions de traitement de texte et ont simplement converti celles multi-octets en UCS-2 et appelaient les autres. Ils remplacent plus tard UCS-2 par UTF-16. Linux, d’autre part, a gardé les codages 8 bits et a donc utilisé UTF-8, car c’est le bon choix dans ce cas.
Mircea Chirea

34
@Pavel Radzivilovsky: BTW, vos écrits sur "Je pense que tous les autres encodages vont finir par mourir. Cela implique que MS-Windows, Java, ICU, python cessent de l'utiliser comme leur favori." et "En particulier, je pense que l'ajout de wchar_t à C ++ était une erreur, de même que les ajouts Unicode à C ++ Ox." sont très naïfs ou très très arrogants. Et cela vient de quelqu'un qui code chez lui avec Linux et qui est satisfait des caractères UTF-8. Pour parler franchement: cela n'arrivera pas .
paercebal

157

Les points de code Unicode ne sont pas des caractères! Parfois, ils ne sont même pas des glyphes (formes visuelles).

Quelques exemples:

  • Les points de code en chiffres romains ressemblent à "ⅲ". (Un seul caractère qui ressemble à "iii".)
  • Les caractères accentués tels que "á", qui peuvent être représentés soit par un seul caractère combiné "\ u00e1", soit par un caractère et un caractère diacritique séparé "\ u0061 \ u0301".
  • Caractères comme Greek minuscule sigma, qui ont des formes différentes pour les positions de milieu ("σ") et fin ("ς"), mais qui doivent être considérés comme des synonymes pour la recherche
  • Tiret facultatif Unicode U + 00AD, qui peut ou non être affiché visuellement, en fonction du contexte, et qui est ignoré pour la recherche sémantique.

La seule façon d'obtenir une édition Unicode correcte consiste à utiliser une bibliothèque écrite par un expert ou à devenir un expert et à en écrire une vous-même. Si vous ne faites que compter les points de code, vous vivez dans un état de péché.


19
Cette. Beaucoup cela. UTF-16 peut causer des problèmes, mais même en utilisant UTF-32 peut toujours (et va) toujours vous donner des problèmes.
bcat

11
Qu'est-ce qu'un personnage? Vous pouvez définir un point de code comme un personnage et vous en tirer très bien. Si vous parlez d'un glyphe visible par l'utilisateur, c'est autre chose.
tchrist

7
@tchrist sure pour allouer de l'espace cette définition est bonne, mais pour toute autre chose? Pas tellement. Si vous gérez un caractère de combinaison en tant que caractère unique (par exemple, pour une opération de suppression ou "prendre les premiers N caractères"), vous obtiendrez un comportement étrange et erroné. Si un point de code n'a de signification que lorsqu'il est combiné avec au moins un autre, vous ne pouvez pas le gérer seul de manière raisonnable.
Voo

6
@ Pacerier, la soirée a pris du retard, mais je dois faire un commentaire à ce sujet. Certaines langues ont un très grand nombre de combinaisons potentielles de signes diacritiques (cf vietnamien, c’est-à-dire mệt). Avoir des combinaisons plutôt qu'un caractère par diacritique est très utile.
asthasr

21
une petite note sur la terminologie: codepoints ne correspondent à des caractères unicode ; Ce que Daniel parle ici, ce sont des caractères perçus par l'utilisateur , qui correspondent à des grappes de graphèmes unicode
Christoph le

54

Il existe une règle simple sur le formulaire de transformation Unicode (UTF) à utiliser: - utf-8 pour le stockage et la communication - utf-16 pour le traitement des données - vous pouvez utiliser utf-32 si la plupart des API de plate-forme que vous utilisez sont utf-32 (commun dans le monde UNIX).

La plupart des systèmes actuels utilisent utf-16 (Windows, Mac OS, Java, .NET, ICU, Qt). Voir aussi ce document: http://unicode.org/notes/tn12/

Retour à "UTF-16 en tant que nocif", je dirais: certainement pas.

Les personnes qui ont peur des substituts (pensant transformer Unicode en un codage de longueur variable) ne comprennent pas les complexités de l’autre (bien plus grandes) qui rendent très complexe le mappage entre les caractères et un point de code Unicode: combinaison de caractères, ligatures, sélecteurs de variation , caractères de contrôle, etc.

Il suffit de lire cette série ici http://www.siao2.com/2009/06/29/9800913.aspx et de voir comment le format UTF-16 devient un problème facile.


26
Ajoutez quelques exemples où UTF-32 est commun dans le monde UNIX!
maxschlepzig

48
Non, vous ne voulez pas utiliser UTF-16 pour le traitement des données. C'est une douleur dans le cul. Il présente tous les inconvénients d'UTF-8 mais aucun de ses avantages. UTF-8 et UTF-32 sont tous deux nettement supérieurs au piratage pervers précédemment connu sous le nom de Mme UTF-16, dont le nom de jeune fille était UCS-2.
tchrist

34
Hier, je viens de trouver un bogue dans la equalsIgnoreCaseméthode de la classe Java Core String (ainsi que d’autres dans la classe String) qui n’aurait jamais existé si Java avait utilisé UTF-8 ou UTF-32. Il y a des millions de ces bombes endormies dans n'importe quel code qui utilise le format UTF-16, et j'en ai marre. UTF-16 est une variole vicieuse qui envahit notre logiciel de bogues insidieux pour toujours. Il est clairement nocif et devrait être déconseillé et interdit.
tchrist

7
@tchrist Wow, donc une fonction non subrogée (car elle a été écrite quand il n'y en avait pas et est malheureusement documentée de manière à rendre impossible l'adaptation - elle spécifie .toUpperCase (char)) provoquera le mauvais comportement? Vous êtes conscient qu'une fonction UTF-32 avec une carte de points de code obsolète ne gèrerait pas mieux cela? De plus, l'API Java entière ne gère pas très bien les substituts et les points les plus complexes sur Unicode pas du tout - et avec le dernier point, l'encodage utilisé n'aurait plus aucune importance.
Voo

8
-1: Un inconditionnel .Substring(1)dans .NET est un exemple trivial de quelque chose qui rompt la prise en charge de tous les Unicode non BMP. Tout ce qui utilise UTF-16 a ce problème; il est trop facile de le traiter comme un codage à largeur fixe et vous voyez trop rarement des problèmes. Cela en fait un encodage nuisible si vous souhaitez prendre en charge Unicode.
Roman Starkov

43

Oui absolument.

Pourquoi? Cela a à voir avec l' exercice du code .

Si vous examinez les statistiques d'utilisation des points de code d'un grand corpus de Tom Christiansen, vous constaterez que les points de code BMP trans-8 bits sont utilisés avec plusieurs ordres si leur ampleur est supérieure à celle des points de code 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

Prenez le dicton TDD: "Le code non testé est un code cassé" et reformulez-le ainsi: "le code non exercé est un code cassé" et pensez à la fréquence à laquelle les programmeurs doivent traiter des points de code non BMP.

Les bogues liés au fait de ne pas traiter l'UTF-16 en tant qu'encodage à largeur variable sont beaucoup plus susceptibles de passer inaperçus que les bogues équivalents dans UTF-8 . Certains langages de programmation ne garantissent toujours pas l’utilisation de UTF-16 à la place de UCS-2, et certains langages de programmation de haut niveau offrent un accès aux unités de code au lieu de points de code (même le C est censé vous donner accès à codepoints si vous utilisez wchar_t, indépendamment de ce que certaines plates-formes peuvent faire).


16
"Les bogues liés au fait de ne pas traiter l'UTF-16 en tant qu'encodage à largeur variable sont beaucoup plus susceptibles de passer inaperçus que les bogues équivalents dans UTF-8." C’est le cœur du problème et, par conséquent, la bonne réponse.
Sean McMillan

3
Précisément. Si la manipulation de votre UTF-8 est bouchée, ce sera immédiatement évident. Si la manipulation de votre UTF-8 est bouchée, vous ne le remarquerez que si vous mettez des caractères Han ou des symboles mathématiques inhabituels.
Escargot mécanique

1
Très vrai, mais d’autre part, à quoi servent les tests unitaires si vous devez dépendre de la chance pour trouver des bugs sur des cas moins fréquents?
musiphil

@musiphil: alors, quand avez-vous créé un test unitaire pour les caractères non-BMP pour la dernière fois?
Ninjalj

1
Pour élaborer sur ma déclaration précédente: même avec UTF-8, vous ne pouvez pas être sûr que vous avez couvert tous les cas après avoir seulement vu quelques exemples de travail. Idem pour UTF-16: vous devez vérifier si votre code fonctionne à la fois avec les non-mères et les mères. (Quelqu'un pourrait même affirmer que l'UTF-8 a au moins quatre affaires majeures, alors que l'UTF-16 n'en a que deux.)
musiphil le

40

Je suggérerais que penser que UTF-16 puisse être considéré comme préjudiciable signifie que vous devez acquérir une meilleure compréhension de Unicode .

Depuis que j'ai été critiqué pour avoir présenté mon opinion sur une question subjective, laissez-moi élaborer. Qu'est-ce qui vous gêne au sujet de l'UTF-16? Préféreriez-vous que tout soit encodé en UTF-8? UTF-7? Ou que diriez-vous de UCS-4? Bien sûr, certaines applications ne sont pas conçues pour gérer tout code à caractère unique, mais elles sont nécessaires, en particulier dans le domaine de l'information global, pour la communication entre frontières internationales.

Mais vraiment, si vous pensez que UTF-16 devrait être considéré comme nuisible parce que cela crée de la confusion ou peut être mal appliqué (unicode peut certainement l'être), alors quelle méthode de codage de caractère serait considérée comme non nuisible?

EDIT: Pour clarifier: Pourquoi une implémentation incorrecte d’une norme est-elle le reflet de la qualité de la norme elle-même? Comme d'autres l'ont noté par la suite, le simple fait qu'une application utilise un outil de manière inappropriée ne signifie pas que l'outil lui-même est défectueux. Si tel était le cas, nous pourrions probablement dire des choses comme "mot clé var considéré comme nuisible" ou "threading considéré comme nuisible". Je pense que la question confond la qualité et la nature de la norme avec les difficultés rencontrées par de nombreux programmeurs pour la mettre en œuvre et l’utiliser correctement, ce qui, selon moi, découle davantage de leur manque de compréhension du fonctionnement de l’unicode, plutôt que de l’unicode lui-même.


33
-1: Que diriez-vous de répondre à certaines des objections d'Artyom, plutôt que de simplement le protéger?

8
BTW: Lorsque j'ai commencé à écrire cet article, j'avais presque envie d'écrire "Est-ce que Joel dans l'article Unicode de Softeare devrait être considéré comme dangereux", car il y a beaucoup d' erreurs. Par exemple: le codage utf-8 prend jusqu'à 4 caractères et non pas 6. De plus, il ne fait pas la distinction entre UCS-2 et UTF-16 qui sont vraiment différents - et causent en fait les problèmes dont je parle.

32
En outre, il convient de noter que lorsque Joel a écrit cet article, la norme UTF-8 était de 6 octets WAS et non de 4. La RFC 3629 a modifié la norme en 4 octets plusieurs mois APRÈS la rédaction de l'article. Comme la plupart des sites Internet, il est utile de lire de plusieurs sources et de connaître l’âge de vos sources. Le lien n'était pas destiné à être la "fin soit tout", mais plutôt un point de départ.

7
Je voudrais pic: utf-8 ou utf-32 qui sont: encodage à longueur variable dans presque tous les cas (y compris BMP) ou encodage à longueur fixe toujours.

18
@iconiK: Ne sois pas stupide. UTF-16 n'est absolument pas la norme de facto pour le traitement de texte. Montrez-moi une langue de programmation plus adaptée au traitement de texte que Perl, qui utilise depuis toujours (depuis plus de 10 ans) des caractères abstraits avec une représentation UTF-8 sous-jacente en interne. Pour cette raison, chaque programme Perl gère automatiquement l’ensemble des caractères Unicode sans que l’utilisateur n’ait à constamment se disputer avec des substituts idiots. La longueur d'une chaîne correspond à son compte en points de code et non en unités de code. Tout le reste est une pure stupidité qui met la compatibilité en amont dans le sens inverse.
tchrist

37

Il n'y a rien de mal avec le codage Utf-16. Mais les langues qui traitent les unités 16 bits comme des caractères devraient probablement être considérées comme mal conçues. Avoir un type nommé ' char' qui ne représente pas toujours un caractère est assez déroutant. Étant donné que la plupart des développeurs s'attendent à ce qu'un type de caractère représente un point de code ou un caractère, une grande partie du code sera probablement endommagé s'il est exposé à des caractères supérieurs à BMP.

Notez cependant que même en utilisant utf-32 ne signifie pas que chaque point de code 32 bits représentera toujours un caractère. En raison de la combinaison de caractères, un caractère réel peut être constitué de plusieurs points de code. Unicode n'est jamais trivial.

BTW. Il existe probablement la même classe de bogues avec les plates-formes et les applications qui s’attendent à ce que les caractères soient de 8 bits, alimentés par Utf-8.


12
Dans le cas de Java, si vous examinez leur chronologie ( java.com/fr/javahistory/timeline.jsp ), vous constaterez que le développement principal de String a eu lieu alors que le format Unicode était de 16 bits (il a changé en 1996). Ils devaient maîtriser la capacité de gérer des points de code non BMP, d'où la confusion.
Kathy Van Stone le

10
@ Kathy: Ce n'est pas vraiment une excuse pour C #. De manière générale, je suis d’accord pour dire qu’il devrait exister un CodePointtype contenant un seul point de code (21 bits), un CodeUnittype contenant une seule unité de code (16 bits pour UTF-16) et un Charactertype devrait idéalement prendre en charge un graphème complet. Mais cela le rend fonctionnellement équivalent à un String...
Joey

1
Cette réponse a presque deux ans, mais je ne peux m'empêcher de commenter. "Avoir un type nommé 'char' qui ne représente pas toujours un caractère est assez déroutant." Et pourtant, les gens l'utilisent tout le temps en C, etc., pour représenter des données entières pouvant être stockées dans un seul octet.
JAB

Et j'ai vu beaucoup de code C qui ne gère pas correctement l'encodage des caractères.
dan04

1
C # a une excuse différente: il a été conçu pour Windows et Windows a été construit sur UCS-2 (il est très gênant de constater que même les API Windows ne peuvent pas prendre en charge UTF-8). De plus, je pense que Microsoft souhaitait la compatibilité Java (.NET 1.0 disposait d'une bibliothèque de compatibilité Java, mais ils ont abandonné le support Java très rapidement - je suppose que cela est dû au procès intenté par Sun contre MS?)
Qwertie

20

Mon choix personnel est de toujours utiliser le format UTF-8. C'est la norme sous Linux pour presque tout. Il est rétrocompatible avec de nombreuses applications existantes. Il existe une surcharge très minime en termes d'espace supplémentaire utilisé pour les caractères non latins par rapport aux autres formats UTF, et une économie d'espace importante pour les caractères latins. Sur le Web, les langues latines règnent en maître et je pense qu’elles le feront dans un avenir prévisible. Et pour répondre à l’un des arguments principaux de la publication originale: presque tous les programmeurs savent qu’UTF-8 comportera parfois des caractères multi-octets. Tout le monde ne traite pas cela correctement, mais ils sont généralement au courant, ce qui est plus que ce qui peut être dit pour UTF-16. Mais, bien sûr, vous devez choisir celui qui convient le mieux à votre application. C'est pourquoi il y en a plus d'un en premier lieu.


3
UTF-16 est plus simple pour tout ce qui est à l'intérieur de BMP, c'est pourquoi il est utilisé si largement. Mais je suis aussi un fan d’UTF-8, qui n’a aucun problème avec l’ordre des octets, ce qui fonctionne à son avantage.
Malcolm

2
Théoriquement oui. En pratique, il existe des choses telles que, par exemple, UTF-16BE, ce qui signifie UTF-16 en big endian sans nomenclature. Ce n'est pas quelque chose que j'ai inventé, c'est un encodage réel autorisé dans les tags ID3v2.4 (les tags ID3v2 sont nuls, mais sont malheureusement largement utilisés). Et dans de tels cas, vous devez définir le caractère final en externe, car le texte lui-même ne contient pas de nomenclature. UTF-8 est toujours écrit dans un sens et ne pose pas ce problème.
Malcolm

23
Non, UTF-16 n'est pas plus simple. C'est plus dur. Cela vous induit en erreur et vous trompe en vous faisant croire que la largeur est fixe. Tout ce code est cassé et tout le reste parce que vous ne le remarquerez pas avant qu'il ne soit trop tard. CASE IN POINT: Je viens de trouver un autre bogue UTF-16 stupide dans les bibliothèques du noyau Java hier, cette fois dans String.equalsIgnoreCase, qui était laissé dans le bogue de UCS-2, et échoue donc sur 16/17 points de code Unicode valides. Depuis combien de temps ce code existe-t-il? Aucune excuse pour que ce soit buggy. UTF-16 mène à la pure stupidité et à un accident imminent. Courir en hurlant de UTF-16.
tchrist

3
@tchrist Il faut être un développeur très ignorant pour ne pas savoir que UTF-16 n'est pas à longueur fixe. Si vous commencez par Wikipedia, vous lirez ce qui suit en haut: "Il produit un résultat de longueur variable correspondant à une ou deux unités de code 16 bits par point de code". La FAQ Unicode dit la même chose: unicode.org/faq//utf_bom.html#utf16-1 . Je ne sais pas, comment UTF-16 peut tromper n'importe qui s'il est écrit partout qu'il est de longueur variable. Quant à la méthode, elle n’a jamais été conçue pour UTF-16 et ne devrait pas être considérée comme Unicode, aussi simple que cela.
Malcolm

2
@tchrist Avez-vous une source pour vos statistiques? Bien que si les bons programmeurs soient rares, je pense que c'est bon, car nous avons plus de valeur. :) En ce qui concerne les API Java, les composants basés sur des caractères peuvent éventuellement devenir obsolètes, mais cela ne garantit pas qu'ils ne seront pas utilisés. Et ils ne seront certainement pas supprimés pour des raisons de compatibilité.
Malcolm

18

Eh bien, il existe un encodage qui utilise des symboles de taille fixe. Je veux certainement dire UTF-32. Mais 4 octets pour chaque symbole, c'est trop d'espace perdu, pourquoi l'utiliserions-nous dans des situations de tous les jours?

Selon moi, la plupart des problèmes découlent du fait que certains logiciels ont pris du retard par rapport à la norme Unicode, mais qu’ils n’ont pas été rapides à corriger la situation. Opera, Windows, Python, Qt - ils sont tous apparus avant que UTF-16 ne soit largement connu ou même né. Je peux toutefois confirmer que dans Opera, Windows Explorer et le Bloc-notes, les problèmes avec les caractères extérieurs à BMP ne sont plus d'actualité (du moins sur mon PC). Quoi qu'il en soit, si les programmes ne reconnaissent pas les paires de substitution, ils n'utilisent pas UTF-16. Quels que soient les problèmes rencontrés lors de l'utilisation de tels programmes, ils n'ont rien à voir avec le format UTF-16.

Cependant, je pense que les problèmes des logiciels existants avec uniquement le support BMP sont quelque peu exagérés. Les caractères hors BMP ne se rencontrent que dans des cas et des zones très spécifiques. Selon la FAQ officielle Unicode , "même dans les textes en Asie de l’Est, l’incidence des paires de substitution devrait représenter bien moins de 1% de l’ensemble du stockage de texte en moyenne". Bien entendu, les caractères extérieurs à BMP ne doivent pas être négligés car un programme n'est pas conforme à Unicode, mais la plupart des programmes ne sont pas conçus pour travailler avec des textes contenant de tels caractères. C'est pourquoi s'ils ne l'appuient pas, c'est désagréable, mais pas une catastrophe.

Considérons maintenant l'alternative. Si UTF-16 n'existait pas, le codage ne conviendrait pas pour les textes non-ASCII et tous les logiciels créés pour UCS-2 devraient être entièrement repensés pour rester compatibles avec Unicode. Ce dernier ne ralentirait probablement que l’adoption de l’Unicode. De plus, nous n'aurions pas pu maintenir la compatibilité avec le texte dans UCS-2 comme le fait UTF-8 en ce qui concerne ASCII.

Maintenant, en mettant de côté toutes les questions héritées, quels sont les arguments contre le codage lui-même? Je doute vraiment que les développeurs de nos jours ne sachent pas que UTF-16 a une longueur variable, il est écrit partout en commençant par Wikipedia. UTF-16 est beaucoup moins difficile à analyser que UTF-8, si quelqu'un a signalé la complexité comme un problème possible. De plus, il est faux de penser qu'il est facile de gâcher la détermination de la longueur de chaîne uniquement en UTF-16. Si vous utilisez UTF-8 ou UTF-32, vous devez toujours savoir qu'un point de code Unicode ne signifie pas nécessairement un caractère. En dehors de cela, je ne pense pas qu'il y ait quelque chose de substantiel contre le codage.

Par conséquent, je ne pense pas que le codage lui-même devrait être considéré comme nuisible. UTF-16 est un compromis entre simplicité et compacité. Il n'y a pas de mal à utiliser ce qui est nécessaire là où il le faut . Dans certains cas, vous devez rester compatible avec ASCII et UTF-8, dans certains cas, vous souhaitez travailler avec les idéogrammes han et conserver de l'espace en utilisant UTF-16, dans certains cas, vous avez besoin de représentations universelles de caractères. encodage de longueur. Utilisez ce qui est plus approprié, faites-le correctement.


21
C'est un point de vue anglo-centrique plutôt délicat, Malcolm. Presque au même niveau que "l'ASCII est suffisant pour les États-Unis - le reste du monde devrait nous convenir".
Jonathan Leffler

28
En fait, je viens de Russie et je rencontre tout le temps des cyrilliques (y compris mes propres programmes), alors je ne pense pas avoir une vision anglo-centrique. :) Mentionner ASCII n'est pas tout à fait approprié, car ce n'est pas Unicode et ne supporte pas les caractères spécifiques. Les formats UTF-8, UTF-16 et UTF-32 prennent en charge les mêmes jeux de caractères internationaux. Ils sont uniquement destinés à être utilisés dans des domaines spécifiques. Et c’est exactement ce que je veux dire: si vous utilisez surtout l’anglais, utilisez UTF-8, si vous utilisez surtout des cyrillics, utilisez UTF-16, si vous utilisez des langues anciennes, utilisez UTF-32. Assez facile.
Malcolm

16
"Ce n'est pas vrai, les scripts asiatiques tels que le japonais, le chinois ou l'arabe appartiennent également à BMP. BMP est en réalité très volumineux et suffisamment grand pour inclure tous les scripts utilisés de nos jours" Tout cela est si faux. BMP contient des caractères 0xFFFF (65536). Le chinois seul a plus que cela. Les normes chinoises (GB 18030) ont plus que cela. Unicode 5.1 a déjà alloué plus de 100 000 caractères.

12
@Marcolm: "Le format BMP est en réalité très volumineux et suffisamment grand pour inclure tous les scripts utilisés de nos jours" Faux. À ce stade, Unicode a déjà attribué environ 100 000 caractères, bien plus que ce que BMP peut prendre en charge. Il y a de gros morceaux de caractères chinois en dehors de BMP. Et certains d'entre eux sont requis par GB-18030 (norme chinoise obligatoire). D'autres sont requises par les normes (non obligatoires) japonaises et coréennes. Donc, si vous essayez de vendre quoi que ce soit sur ces marchés, vous avez besoin d'une assistance au-delà de BMP.

8
Tout ce qui utilise UTF-16 mais ne peut gérer que des caractères BMP étroits n’utilise pas réellement UTF-16. C'est buggy et cassé. La prémisse de l'OP est solide: UTF-16 est préjudiciable, car il amène les naïfs à écrire du code erroné. Soit vous pouvez gérer le texte Unicode, soit vous ne le pouvez pas. Si vous ne pouvez pas le faire, vous choisissez un sous-ensemble, ce qui est aussi stupide qu'un traitement de texte au format ASCII uniquement.
tchrist

16

Des années d’internationalisation du travail de Windows, en particulier dans les langues d’Asie orientale, m’auraient peut-être corrompu, mais je me tourne davantage vers UTF-16 pour les représentations de chaînes internes au programme et UTF-8 pour le stockage en réseau ou sur fichier de documents de type texte en clair. UTF-16 peut généralement être traité plus rapidement sous Windows, c’est donc le principal avantage de l’utilisation de UTF-16 sous Windows.

Le passage à la norme UTF-16 a considérablement amélioré l’adéquation des produits moyens traités avec du texte international. Il n'y a que quelques cas étroits dans lesquels les paires de substitution doivent être prises en compte (suppressions, insertions et sauts de ligne, en gros) et le cas moyen est généralement direct. Et contrairement aux encodages antérieurs tels que les variantes JIS, UTF-16 limite les paires de substitution à une plage très étroite, de sorte que la vérification est très rapide et fonctionne dans les deux sens.

Certes, il est aussi rapide en UTF-8 correctement codé. Mais il existe également de nombreuses applications UTF-8 cassées qui codent de manière incorrecte des paires de substitution sous forme de deux séquences UTF-8. Donc, UTF-8 ne garantit pas le salut non plus.

IE gère assez bien les paires de substitution depuis 2000 environ, même s'il les convertit généralement des pages UTF-8 en une représentation interne UTF-16; Je suis à peu près sûr que Firefox a bien compris, donc je me fiche de ce que fait Opera.

UTF-32 (alias UCS4) est inutile pour la plupart des applications car il nécessite peu d’espace et qu’il s’agit donc d’un nonstarter.


6
Je n'ai pas très bien compris votre commentaire sur UTF-8 et les paires de substitution. Les paires de substitution ne sont qu'un concept significatif dans le codage UTF-16, n'est-ce pas? Peut-être que le code qui convertit directement l'encodage UTF-16 en encodage UTF-8 pourrait obtenir cette erreur, et dans ce cas, le problème consiste à lire de manière incorrecte l'UTF-16 et non à l'écrire UTF-8. Est-ce correct?
Craig McQueen

11
Jason parle d'un logiciel qui implémente délibérément UTF-8 de cette façon: créez une paire de substitution, puis UTF-8 encodera chaque moitié séparément. Le nom correct pour cet encodage est CESU-8, mais Oracle (par exemple) le présente sous la forme UTF-8. Java utilise un schéma similaire pour la sérialisation des objets, mais il est clairement documenté sous le nom "Modified UTF-8" et uniquement pour un usage interne. (Maintenant, si nous pouvions simplement amener les gens à LIRE cette documentation et à cesser d'utiliser DataInputStream # readUTF () et DataOutputStream # writeUTF () de manière inappropriée ...)

Autant que je sache, UTF-32 est toujours un codage à longueur variable et différent de UCS4, qui correspond à une plage spécifique de points de code.
Eonil

@Eonil, le format UTF-32 ne sera jamais distinct de UCS4 que si nous avons une norme Unicode comportant quelque chose comme un UCS5 ou supérieur.
JasonTrue

@JasonTrue Pourtant, seuls les résultats sont égaux par coïncidence, non garantis par la conception. La même chose s'est produite avec l'adressage mémoire 32 bits, Y2K, UTF16 / UCS2. Ou avons-nous une garantie de cette égalité? Si nous avons, je serais heureux de l'utiliser. Mais je ne veux pas écrire un code cassable possible . J'écris un code de niveau de caractère et l'absence d'un moyen garanti de transcoder entre les points de code UTF <-> me perturbe beaucoup.
Eonil

16

UTF-8 est définitivement le chemin à parcourir, éventuellement accompagné de UTF-32 pour une utilisation interne dans les algorithmes nécessitant un accès aléatoire hautes performances (mais qui ignore la combinaison de caractères).

UTF-16 et UTF-32 (ainsi que leurs variantes LE / BE) souffrent de problèmes de réseau, ils ne doivent donc jamais être utilisés à l'extérieur.


9
L'accès aléatoire à temps constant est également possible avec UTF-8, utilisez simplement des unités de code plutôt que des points de code. Vous avez peut-être besoin d'un véritable accès de code aléatoire, mais je n'ai jamais vu de cas d'utilisation, et vous êtes tout aussi susceptible de vouloir un accès aléatoire à une grappe de graphèmes.

15

UTF-16? définitivement nuisible. Juste mon grain de sel ici, mais il y a exactement trois encodages acceptables pour du texte dans un programme:

  • ASCII: lorsque vous travaillez avec des choses de bas niveau (par exemple, des microcontrôleurs) qui ne peuvent vous offrir mieux
  • UTF8: stockage sur des supports de largeur fixe tels que des fichiers
  • nombres de codes entiers ("CP"?): un tableau des entiers les plus grands qui conviennent à votre langage de programmation et à votre plate-forme (décomposition en ASCII dans la limite des faibles ressources). Doit être int32 sur les ordinateurs plus anciens et int64 sur tout ce qui a un adressage 64 bits.

  • De toute évidence, les interfaces avec le code existant utilisent le codage nécessaire pour que l'ancien code fonctionne correctement.


4
@ simon buchan, le U+10ffffmax sortira de la fenêtre quand (pas si) ils manqueront de points de code. Cela dit, utiliser int32 sur un système p64 pour la vitesse est probablement sans danger, car je doute qu'ils dépasseront U+ffffffffavant que vous ne soyez obligé de réécrire votre code pour les systèmes 128 bits vers 2050. (C'est le point de "utiliser le plus grand int est pratique "par opposition à" le plus grand disponible "(qui serait probablement int256 ou bignums ou quelque chose).)
David X

1
@ David: Unicode 5.2 code pour 107 361 points de code. Il y a 867 169 codes codés non utilisés. "quand" est juste idiot. Un point de code Unicode est défini comme un nombre compris entre 0 et 0x10FFFF, une propriété dont dépend UTF-16. (Aussi, 2050 semble beaucoup trop bas pour une estimation des systèmes 128 bits lorsqu'un système 64 bits peut contenir la totalité de l'Internet dans son espace d'adressage.)

3
@David: Votre "quand" faisait référence au manque de points de code Unicode, pas à un commutateur de 128 bits qui, oui, le sera dans les prochains siècles. Contrairement à la mémoire, il n'y a pas de croissance exponentielle du nombre de caractères, aussi le consortium Unicode a-t-il spécifiquement garanti qu'il n'allouera jamais de point de code ci-dessus U+10FFFF. C'est vraiment l' une de ces situations où 21 bits est suffisant pour tout le monde.

10
@Simon Buchan: Au moins jusqu'au premier contact. :)

3
Unicode garantissait qu’il n’y aurait pas non plus de points de code au-dessus de U + FFFF.
Shannon Severance

13

Unicode définit des points de code allant jusqu'à 0x10FFFF (1 114 112 codes), toutes les applications fonctionnant dans un environnement multilingue traitant des chaînes / noms de fichiers, etc. doivent le gérer correctement.

Utf-16 : ne couvre que 1 112 064 codes. Bien que ceux situés à la fin de l’ Unicode proviennent des plans 15 à 16 (Zone d’utilisation privée). Il ne peut plus se développer dans le futur si ce n’est briser le concept Utf-16 .

Utf-8 : couvre théoriquement 2 216 757 376 codes. La plage actuelle de codes Unicode peut être représentée par une séquence maximale de 4 octets. Il ne souffre pas du problème d' ordre d'octet , il est "compatible" avec ascii.

Utf-32 : couvre théoriquement 2 ^ 32 = 4 294 967 296 codes. Actuellement, il n'est pas codé en longueur variable et ne le sera probablement pas à l'avenir.

Ces faits sont explicites. Je ne comprends pas préconiser l’usage général de Utf-16 . Il est codé en longueur variable (il n’est pas accessible par index), il a des problèmes pour couvrir toute la plage Unicode , même à l’heure actuelle, l’ordre des octets doit être géré, etc. Je ne vois aucun avantage, sauf qu’il est utilisé nativement dans Windows d'autres lieux. Même si, lors de l’écriture de code multiplate-forme, il est probablement préférable d’utiliser Utf-8 de manière native et d’effectuer des conversions uniquement aux points de terminaison de la manière dépendante de la plate-forme (comme cela a déjà été suggéré). Si l'accès direct par index est nécessaire et que la mémoire n'est pas un problème, vous devez utiliser Utf-32 .

Le principal problème est que de nombreux programmeurs utilisant Windows Unicode = Utf-16 ne savent même pas ou ignorent qu'il s'agit d'un codage à longueur variable.

La manière dont il est généralement utilisé dans la plate-forme * nix est plutôt bonne: chaînes c (char *) interprétées comme codées en Utf-8 , chaînes c larges (wchar_t *) interprétées en tant que Utf-32 .


7
Remarque: UTF-16 couvre tout Unicode car le consortium Unicode a décidé que 10FFFF correspond à la plage TOP d'Unicode et une longueur définie de 4 octets UTF-8 maximale et une plage explicitement exclue 0xD800-0xDFFF de la plage de points de code valide. paires de substitution. Ainsi, tout texte Unicode valide peut être représenté avec chacun de ces codages. Aussi à propos de la croissance à l'avenir. Il ne semble pas qu'un million de points de code ne suffirait pas dans un avenir lointain.

7
@Kerrek: Incorrect: UCS-2 n'est pas un codage Unicode valide. Tous les codages UTF- *, par définition, peuvent représenter n’importe quel point de code Unicode qui est légal pour un échange. UCS-2 peut représenter beaucoup moins que cela, plus un peu plus. Répéter: UCS-2 n’est pas un codage Unicode valide, plus que l’ASCII.
tchrist

1
"Je ne comprends pas prôner l'utilisation générale de Utf-8 . Il est codé en longueur variable (ne peut pas être consulté par index)"
Ian Boyd

9
@ Ian Boyd, la nécessité d'accéder au caractère individuel d'une chaîne dans un modèle d'accès aléatoire est incroyablement exagérée. C’est à peu près aussi courant que de vouloir calculer la diagonale d’une matrice de caractères, ce qui est super rare. Les chaînes sont pratiquement toujours traitées de manière séquentielle, et puisque l'accès à UTF-8 car N + 1 étant donné que vous êtes à UTF-8 , car car N est O (1), il n'y a pas de problème. Il est extrêmement peu nécessaire de faire un accès aléatoire aux chaînes. Si vous pensez que cela vaut la peine de disposer d’un espace de stockage suffisant pour passer à UTF-32 au lieu de UTF-8, c’est votre propre opinion, mais pour moi, cela ne pose aucun problème.
tchrist

2
@tchrist, je vous concéderai que les chaînes sont pratiquement toujours traitées de manière séquentielle si vous incluez l'itération inverse en tant que "séquentielle" et étendez cette comparaison un peu plus loin de l'extrémité inférieure d'une chaîne par rapport à une chaîne connue. Deux scénarios très courants consistent à tronquer les espaces à partir de la fin des chaînes et à vérifier l'extension du fichier à la fin du chemin.
Andy Dent

11

Ajoutez ceci à la liste:

Le scénario présenté est simple (encore plus simple que je le présenterai ici à l’origine!): 1. Une zone de texte WinForms est assise sur un formulaire, vide. Il a une longueur maximale définie sur 20 .

2.L'utilisateur tape dans la zone de texte, ou peut-être y colle du texte.

3. Peu importe ce que vous tapez ou collez dans la zone de texte, vous êtes limité à 20, mais le texte retentit avec sympathie au-delà de 20 (YMMV ici; j'ai modifié mon schéma sonore pour obtenir cet effet!).

4.Le petit paquet de texte est ensuite envoyé ailleurs pour vous lancer dans une aventure passionnante.

Maintenant, il s’agit d’un scénario facile, et tout le monde peut l’écrire pendant son temps libre. Je viens de l'écrire moi-même dans plusieurs langages de programmation sous WinForm, parce que je m'ennuyais et que je ne l'avais jamais essayé auparavant. Et avec du texte dans plusieurs langues car je suis câblé de cette façon et j'ai plus de configurations de clavier que quiconque dans tout l'univers terrifiant.

J'ai même nommé la forme Magic Carpet Ride , pour aider à atténuer l'ennui.

Cela n'a pas fonctionné, pour ce que ça vaut.

Alors, au lieu de cela, j’ai saisi les 20 caractères suivants dans mon formulaire Magic Carpet Ride :

0123401234012340123

Euh oh.

Ce dernier caractère est U + 20000, le premier idéogramme Unicode de type Extension B (U + d840 U + dc00, à ses amis proches à qui il n'a pas honte d'être déshabillé, pour ainsi dire ....).

entrez la description de l'image ici

Et maintenant nous avons un jeu de balle.

Parce que quand TextBox.MaxLength parle de

Obtient ou définit le nombre maximal de caractères pouvant être entrés manuellement dans la zone de texte.

ce que cela signifie vraiment, c'est

Obtient ou définit le nombre maximal d'unités de code UTF-16 LE qui peuvent être entrées manuellement dans la zone de texte et tronque sans pitié la merde vivante de toute chaîne essayant de jouer à des jeux mignons avec la notion de caractère linguistique que seule une personne aussi obsédée ce gars de Kaplan sera offensif (bon sang, il a besoin de sortir plus!).

Je vais essayer de voir si le document est mis à jour ....
Les lecteurs normaux qui se souviendront de ma série UCS-2 à UTF-16 noteront mon mécontentement face à la notion simpliste de TextBox.MaxLength et à la manière dont elle devrait être gérée au minimum. son comportement draconien crée une séquence illégale, une séquence que d’autres parties du .Net Framework peuvent jeter un

  • System.Text.EncoderFallbackException: impossible de traduire le caractère Unicode \ uD850 d'index 0 en page de code spécifiée. *

exception si vous passez cette chaîne ailleurs dans le .Net Framework (comme le faisait mon collègue Dan Thompson).

Bon maintenant, peut-être que la série complète UCS-2 à UTF-16 est hors de portée de beaucoup.
Mais n’est-il pas raisonnable de s’attendre à ce que TextBox.Text ne produise pas System.Stringcela ne causera pas un autre morceau du .Net Framework à jeter? Je veux dire, ce n'est pas comme s'il y avait une chance sous la forme d'un événement sur le contrôle qui vous indique la troncature à venir où vous pouvez facilement ajouter la validation la plus intelligente - une validation que le contrôle lui-même ne craint pas de faire. J'irais même jusqu'à dire que ce contrôle punk enfreint un contrat de sécurité qui pourrait même entraîner des problèmes de sécurité si vous pouviez classer comme causant des exceptions inattendues la résiliation d'une application comme une sorte de déni de service grossier. Pourquoi un processus, une méthode, un algorithme ou une technique WinForms devrait-il produire des résultats invalides?

Source: Michael S. Kaplan Blog MSDN


Merci, très bon lien! Je l'ai ajouté à la liste des problèmes dans la question.

9

Je ne dirais pas nécessairement que l'UTF-16 est nocif. Ce n'est pas élégant, mais il sert à la compatibilité ascendante avec UCS-2, tout comme le GB18030 avec GB2312 et l'UTF-8 avec ASCII.

Cependant, apporter un changement fondamental à la structure d'Unicode en cours de route, après que Microsoft et Sun aient mis au point d'énormes APIs autour de caractères 16 bits, était préjudiciable. L'échec de la sensibilisation au changement était plus préjudiciable.


8
UTF-8 est un sur-ensemble d'ASCII, mais UTF-16 n'est PAS un sur-ensemble d'UCS-2. Bien que ce soit presque un sur-ensemble, un codage correct de UCS-2 en UTF-8 aboutit à l'abomination connue sous le nom de CESU-8; UCS-2 n'a pas de substituts, mais des points de code ordinaires, ils doivent donc être traduits comme tels. Le véritable avantage de l'UTF-16 est qu'il est plus facile de mettre à niveau une base de code UCS-2 qu'une réécriture complète pour UTF-8. Drôle, hein?

1
Bien sûr, techniquement, UTF-16 n'est pas un sur-ensemble d'UCS-2, mais à quel moment U + D800 à U + DFFF ont-ils déjà été utilisés pour autre chose que des substituts de UTF-16?
dan04

2
Peu importe Tout traitement autre que le passage aveugle dans le flux secondaire nécessite de décoder les paires de substitution, ce que vous ne pouvez pas faire si vous le traitez comme UCS-2.

6

UTF-16 est le meilleur compromis entre traitement et espace . C'est pourquoi la plupart des grandes plates-formes (Win32, Java, .NET) l'utilisent pour la représentation interne des chaînes.


31
-1 car UTF-8 sera probablement plus petit ou ne sera pas significativement différent. Pour certains scripts asiatiques, UTF-8 correspond à trois octets par glyphe, alors que UTF-16 ne représente que deux. Toutefois, UTF-8 n’est qu’un octet pour ASCII (qui apparaît souvent même dans les langues asiatiques dans les noms de produits, les commandes, etc.). des choses). En outre, dans lesdites langues, un glyphe contient plus d'informations qu'un caractère latin, il est donc justifié qu'il prenne plus de place.

32
Je ne qualifierais pas de combiner les pires aspects des deux options.

18
Ce n'est pas plus facile que UTF-8. C'est aussi de longueur variable.
luiscubal

36
Laissons de côté les débats sur les avantages de l'UTF-16: Ce que vous avez cité n'est pas la raison pour laquelle Windows, Java ou .NET utilise UTF-16. Windows et Java remontent à une époque où Unicode était un encodage 16 bits. UCS-2 était un choix raisonnable à l'époque. Quand Unicode est devenu un codage 21 bits, la migration vers UTF-16 était le meilleur choix que les plates-formes existantes avaient. Cela n’a rien à voir avec la facilité de manipulation ou les compromis d’espace. C'est juste une question d'héritage.
Joey

10
.NET hérite de l'héritage Windows ici.
Joey le

6

Je n'ai jamais compris l'intérêt de l'UTF-16. Si vous voulez la représentation la moins encombrante, utilisez UTF-8. Si vous voulez pouvoir traiter le texte comme une longueur fixe, utilisez UTF-32. Si vous ne voulez ni l'un ni l'autre, utilisez UTF-16. Pire encore, puisque tous les caractères communs (plan multilingue de base) dans UTF-16 tiennent dans un seul point de code, les bogues qui supposent que UTF-16 est de longueur fixe seront subtils et difficiles à trouver, alors que si vous essayez de le faire Avec UTF-8, votre code échouera rapidement et fort dès que vous tenterez d’internationaliser.


6

Comme je ne peux pas encore commenter, je publie cette réponse en tant que réponse, car il semble que je ne peux pas autrement contacter les auteurs de utf8everywhere.org. Dommage que je n’obtienne pas automatiquement le privilège de commentaire, car j’ai assez de réputation sur d’autres échanges de pile.

Ceci est considéré comme un commentaire à l' opinion: Oui, UTF-16 devrait être considéré comme une réponse nuisible .

Une petite correction:

Pour éviter de faire passer accidentellement un fichier UTF-8 char*dans les versions ANSI-string des fonctions Windows-API, il convient de définir UNICODE, non _UNICODE. _UNICODEfonctions de cartes comme _tcslenà wcslen, non MessageBoxà MessageBoxW. Au lieu de cela, la UNICODEdéfinition prend soin de ce dernier. Pour preuve, cela provient de l'en- WinUser.htête de MS Visual Studio 2005 :

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

Au minimum, cette erreur devrait être corrigée utf8everywhere.org.

Une suggestion:

Peut-être que le guide devrait contenir un exemple d'utilisation explicite de la version Wide-string d'une structure de données, pour le rendre moins facile à manquer / oublier. L'utilisation de versions de chaînes de données Wide-string en plus de l'utilisation de versions de fonctions Wide-string réduit encore les risques d'appeler accidentellement une version ANSI d'une telle fonction.

Exemple de l'exemple:

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

D'accord; Merci! Nous mettrons à jour le document. Le document a encore besoin de développement et d’ajout d’informations sur les bases de données. Nous sommes heureux de recevoir des contributions de libellés.
Pavel Radzivilovsky

@PavelRadzivilovsky _UNICODEest toujours là :(
cubuspl42

Merci pour le rappel. cubus, Jelle, voudriez-vous un utilisateur pour notre SVN?
Pavel Radzivilovsky

@Pavel Bien sûr, l'apprécierait!
Jelle Geerts

@ JelleGeerts: Je m'excuse pour ce retard. Vous pouvez toujours nous contacter par nos emails (lien du manifeste) ou Facebook. Nous sommes faciles à trouver. Bien que je pense que nous ayons résolu le problème que vous avez amené ici (et je vous ai crédité à ce titre), tous les débats UTF-8 vs UTF-16 sont toujours d'actualité. Si vous avez plus à contribuer, n'hésitez pas à nous contacter via ces canaux privés.
Ybungalobill

5

Quelqu'un a dit que UCS4 et UTF-32 étaient les mêmes. Non, mais je sais ce que tu veux dire. L'un d'eux est un encodage de l'autre, cependant. J'aurais aimé qu'ils spécifient l'idée de spécifier l'endianité dès le départ pour ne pas avoir la bataille des endianesses ici aussi. N'avaient-ils pas vu cela venir? Au moins, UTF-8 est identique partout (à moins que quelqu'un ne respecte la spécification d'origine avec 6 octets).

Si vous utilisez UTF-16, vous devez inclure la gestion des caractères multi-octets. Vous ne pouvez pas aller au Nième caractère en indexant 2N dans un tableau d'octets. Vous devez marcher ou avoir des index de caractère. Sinon, vous avez écrit un bug.

La spécification actuelle de C ++ indique que UTF-32 et UTF-16 peuvent avoir des variantes little-endian, big-endian et non spécifiée. Vraiment? Si Unicode avait spécifié que tout le monde devait faire du little-endian depuis le début, tout aurait été plus simple. (J'aurais bien aimé le big-endian également.) Au lieu de cela, certaines personnes l'ont mis en œuvre d'une manière, d'une autre, et maintenant nous sommes coincés avec de la bêtise pour rien. Parfois, il est embarrassant d'être un ingénieur en logiciel.


Une adresse non spécifiée est supposée inclure BOM en tant que premier caractère, utilisé pour déterminer le sens de lecture de la chaîne. UCS-4 et UTF-32 sont en effet les mêmes de nos jours, à savoir une valeur numérique UCS comprise entre 0 et 0x10FFFF stockée dans un entier de 32 bits.

5
@Tronic: Techniquement, ce n'est pas vrai. Bien qu'UCS-4 puisse stocker un entier de 32 bits, il est interdit à UTF-32 de stocker les points de code non-caractères interdits pour l'échange, tels que 0xFFFF, 0xFFFE et tous les substituts. UTF est un codage de transport, pas un codage interne.
tchrist

Les problèmes d'endurance sont inévitables tant que différents processeurs continuent à utiliser différents ordres d'octet. Cependant, cela aurait pu être bien s'il y avait un ordre d'octets "préféré" pour le stockage de fichiers UTF-16.
Qwertie

Même si UTF-32 est à largeur fixe pour les points de code , il ne l'est pas à largeur fixe pour les caractères . (Vous entendez quelque chose appelé "combinaison de caractères"?) Donc vous ne pouvez pas aller au caractère N'th en indexant simplement 4N dans le tableau d'octets.
musiphil

2

Je ne pense pas que ce soit nocif si le développeur est suffisamment prudent.
Et ils devraient accepter ce compromis s’ils le savent aussi.

En tant que développeur de logiciels japonais, je trouve UCS-2 assez volumineux et limiter l’espace simplifie apparemment la logique et réduit la mémoire d’exécution. Il est donc suffisant d’utiliser utf-16 sous la limitation UCS-2.

Il existe un système de fichiers ou une autre application qui suppose que les points de code et les octets sont proportionnels, ce qui permet de garantir que le nombre brut de points de code est ajusté à un stockage de taille fixe.

Par exemple, NTFS et VFAT spécifient UCS-2 comme codage de stockage du nom de fichier.

Si ces exemples veulent vraiment étendre au support UCS-4, je pourrais accepter l'utilisation d'utf-8 pour tout, mais la longueur fixe a de bons points comme:

  1. peut garantir la taille par longueur (la taille des données et la longueur du point de code sont proportionnelles)
  2. peut utiliser le numéro d'encodage pour la recherche de hachage
  3. les données non compressées ont une taille raisonnable (par rapport à utf-32 / UCS-4)

Dans le futur, lorsque la mémoire / la puissance de traitement ne coûteront pas cher, même dans les périphériques intégrés, nous pourrons accepter le périphérique comme étant un peu lent pour éviter les erreurs de cache, les erreurs de page et l'utilisation de mémoire supplémentaire, mais cela n'arrivera pas dans un avenir proche, je suppose ...


3
Pour ceux qui liront ce commentaire, il est intéressant de noter que UCS-2 n’est pas la même chose que UTF-16. Veuillez rechercher les différences pour comprendre.
mikebabcock

1

"L’un des encodages les plus populaires, UTF-16, doit-il être considéré comme dangereux?"

Très probablement, mais les alternatives ne doivent pas nécessairement être considérées comme étant bien meilleures.

La question fondamentale est qu’il existe de nombreux concepts différents concernant: les glyphes, les caractères, les points de code et les séquences d’octets. La correspondance entre chacun de ces éléments n’est pas triviale, même à l’aide d’une bibliothèque de normalisation. (Par exemple, certains caractères dans les langues européennes écrits avec un script basé sur le latin ne sont pas écrits avec un seul code codé Unicode. Et c'est à la fin de la complexité!) Ce que cela signifie est que pour que tout soit correct, il est assez surprenant difficile; il faut s'attendre à des bugs bizarres (et au lieu de simplement s'en plaindre ici, informez-en les responsables du logiciel concerné).

Le seul moyen de considérer l'UTF-16 comme dangereux, par opposition à l'UTF-8, par exemple, consiste à coder différemment les points de code en dehors du BMP (en tant que paire de substituts). Si le code souhaite accéder ou itérer par point de code, cela signifie qu'il doit être conscient de la différence. OTOH, cela signifie qu’un corps substantiel de code existant qui suppose que les "caractères" peuvent toujours être insérés dans une quantité de deux octets - une hypothèse assez courante, voire erronée - peut au moins continuer à fonctionner sans tout reconstruire. En d'autres termes, au moins, vous pouvez voir ces caractères qui ne sont pas gérés correctement!

Je voudrais retourner votre question et dire que tout le foutu shebang d'Unicode devrait être considéré comme nuisible et que tout le monde devrait utiliser un codage en 8 bits, sauf que j'ai vu (au cours des 20 dernières années) où cela mène: horrible confusion sur les divers codages ISO 8859, ainsi que sur l’ensemble des codages utilisés pour Cyrillic et la suite EBCDIC, et… eh bien, Unicode pour tous ses défauts est supérieur à celui. Si seulement ce n'était pas un compromis aussi désagréable entre les malentendus des différents pays.


Connaissant notre chance, dans quelques années, nous serons à court d'espace UTF-16. Meh
Donal Fellows

3
La question fondamentale est que le texte est d'une dureté trompeuse. Aucune approche permettant de représenter cette information de manière numérique ne peut être simple. C'est la même raison pour laquelle les dates sont difficiles, les calendriers sont difficiles, le temps est dur, les noms personnels sont difficiles, les adresses postales sont difficiles: chaque fois que les machines numériques se croisent avec des constructions culturelles humaines, la complexité éclate. C'est un fait de la vie. Les humains ne fonctionnent pas sur la logique numérique.
Aristote Pagaltzis
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.