2010-10-16 7 views
10

J'ai lu quelques articles sur les meilleures pratiques pour les chaînes et l'encodage de caractères en C++, mais j'ai du mal à trouver une approche générale qui me semble raisonnablement simple et correcte. Puis-je demander des commentaires sur les points suivants? Je suis enclin à utiliser UTF-8 et UTF-32, et de définir quelque chose comme:Chaînes et codage de caractères en C++

typedef std::string string8; 
typedef std::basic_string<uint32_t> string32; 

La classe chaîne8 serait utilisé pour UTF-8, et ayant un type distinct est juste un rappel de l'encodage . Une alternative serait que string8 soit une sous-classe de std :: string et supprime les méthodes qui ne conviennent pas à UTF-8.

La classe string32 serait utilisée pour UTF-32 lorsqu'une taille de caractères fixe est souhaitée.

Les fonctions CPT UTF-8, utf8 :: utf8to32() et utf8 :: utf32to8(), ou encore des fonctions wrapper plus simples, seraient utilisées pour convertir entre les deux.

+0

Notez que 'string8' est toujours du même type que' std :: string'; il a juste un nom différent. –

+0

Quelles fonctions 'std :: basic_string' * sont * correctes pour UTF-8? – dalle

+0

Qu'est-ce que UTF-32 vous achète sur wstring/Unicode? btw Visual Studio définit 'u16string' et' u32string'. –

Répondre

9

Si vous prévoyez de simplement passer des chaînes et de ne jamais les inspecter, vous pouvez utiliser std::string bien que ce soit un travail médiocre. Le problème est que la plupart des frameworks, même standard, ont un codage imposé de manière stupide (je pense) en mémoire. Je dis stupide parce que le codage devrait seulement importer sur l'interface, et ces codages ne sont pas adaptés pour la manipulation en mémoire des données.

De plus, l'encodage est facile (c'est une simple transposition CodePoint -> octets et inversement) alors que la difficulté principale est en fait de manipuler les données.

Avec un 8 bits ou 16 bits, vous risquez de couper un caractère au milieu car ni std::string ni std::wstring ne savent ce qu'est un caractère Unicode. Pire, même avec un encodage 32 bits, il y a le risque de séparer un personnage des signes diacritiques qui lui sont applicables, ce qui est aussi stupide.

La prise en charge d'Unicode en C++ est donc extrêmement inférieure, en ce qui concerne la norme.

Si vous souhaitez vraiment manipuler la chaîne Unicode, vous avez besoin d'un conteneur compatible avec Unicode. La manière habituelle est d'utiliser la bibliothèque , bien que son interface soit vraiment C-ish. Cependant, vous aurez tout ce dont vous avez besoin pour travailler en Unicode avec plusieurs langues.

+1

J'ai trouvé votre commentaire sur les signes diacritiques un peu effrayant. C'est en un sens le plus pertinent pour ce que j'essaie de faire, c'est-à-dire de manipuler les cordes "correctement" d'une manière relativement simple. – nassar

+0

@nassar: malheureusement, c'est effrayant parce que nous manquons de support: '( –

+0

ICU a (parmi d'autres interfaces en C++) une classe de chaînes C++ interopérant avec std :: string –

1

L'approche des traits décrite here pourrait être utile. C'est une technique ancienne mais utile.

1

Il n'est pas spécifié quel codage de caractères doit être utilisé pour les chaînes, les chaînes, etc. La manière la plus courante consiste à utiliser unicode dans des chaînes étendues. Quels types et codages doivent être utilisés dépend de vos besoins.

Si vous avez seulement besoin de passer des données de A à B, choisissez std :: string avec encodage UTF-8 (ne pas introduire un nouveau type, utilisez simplement std :: string). Si vous devez travailler avec des chaînes (extraire, concaténer, trier, ...) choisissez std :: wstring et comme encodage UCS2/UTF-16 (BMP uniquement) sous Windows et UCS4/UTF-32 sous Linux. L'avantage est la taille fixe: chaque caractère a une taille de 2 (ou 4 pour UCS4) octets tandis que std :: string avec UTF-8 renvoie des résultats de longueur incorrecte(). Pour la conversion, vous pouvez vérifier sizeof (std :: wstring :: value_type) == 2 ou 4 pour choisir UCS2 ou UCS4. J'utilise la bibliothèque ICU, mais il peut y avoir des bibliothèques wrapper simples.

Dériver de std :: string n'est pas recommandé car basic_string n'est pas conçu pour (manque de membres virtuels, etc.). Si vraiment vous avez vraiment besoin de votre propre type, comme par exemple std :: basic_string my_char_type> écrivez une spécialisation personnalisée pour cela.

La nouvelle norme C++ 0x définit wstring_convert <> et wbuffer_convert <> pour convertir un std :: codecvt à partir d'un jeu de caractères étroit à un large jeu de caractères (par exemple UTF-8 à UCS2). Visual Studio 2010 a déjà implémenté ceci, afaik.

+2

J'ai volontairement évité UCS-2, car il me semble que si l'on se donne la peine de manipuler l'encodage de caractères, on pourrait aussi bien le faire correctement et prendre en charge l'Unicode complet. (En même temps, je cherche quelque chose de moins encombrant que l'ICU pour un usage général.) Comme pour UTF-16, il semble avoir les inconvénients de l'encodage à longueur variable et de beaucoup de mémoire. C'est pourquoi je propose d'utiliser UTF-8 et UTF-32 en combinaison. – nassar

+0

Point pris à propos de dérive de std :: string. Merci! – nassar

+1

Je pense que la définition d'un nouveau type n'est pas du tout essentielle, mais beaucoup de gens qui voient std :: string dans le code auront tendance à oublier les caractères multi-octets et à utiliser incorrectement les positions des caractères. Le fait que ce soit UTF-8 peut être transmis dans les commentaires, mais avoir un rappel dans le nom du type semble utile car des méthodes telles que std :: string :: insert() suggèrent des caractères de 8 bits selon moi. – nassar