96 votes

Comment puis-je utiliser correctement std::string sur UTF-8 en C ?

Ma plate-forme est un Mac et C++11 (ou ci-dessus). Je suis une C++ débutant et travailler sur un projet personnel qui traite le Chinois et l'anglais. UTF-8 est un encodage de préférence pour le présent projet.

J'ai lu quelques posts sur Stack Overflow, et beaucoup d'entre eux vous suggérons d'utiliser std::string lorsqu'ils traitent avec de l'UTF-8 et d'éviter wchar_t comme il n'y a pas d' char8_t dès maintenant pour l'UTF-8.

Cependant, aucun d'entre eux parler de la façon de traiter correctement avec des fonctions comme l' str[i], std::string::size(), std::string::find_first_of() ou std::regex que ces fonction renvoie généralement à des résultats inattendus lorsque face à l'UTF-8.

Dois-je aller de l'avant avec std::string ou passer à l' std::wstring? Si je dois rester avec std::string, quelle est la meilleure pratique pour une pour gérer les problèmes ci-dessus?

147voto

Matthieu M. Points 101624

Glossaire Unicode

Unicode est un vaste et complexe sujet. Je ne souhaite pas wade trop profonde il y a, cependant, un rapide glossaire est nécessaire:

  1. Les Points de Code: les Points de Code sont les blocs de construction de base de l'Unicode, un point de code est juste un entier mappé à un sens. La partie entière s'adapte en 32 bits (bien, 24 bits vraiment), et le sens peut être une lettre, un signe diacritique, un espace, un signe, un smiley, une demi-drapeau, ... et il peut même être "la prochaine partie se lit de droite à gauche".
  2. Graphème Clusters: Graphème les Clusters sont des groupes d'sémantiquement liées Points de Code, par exemple un drapeau dans unicode est représentée par l'association de deux Points de Code; chacun de ces deux, dans l'isolement, n'a pas de sens, mais associées dans un Graphème Cluster ils représentent un drapeau. Graphème Clusters sont également utilisés pour paire une lettre avec un signe diacritique dans certains scripts.

C'est la base de la norme Unicode. La distinction entre le Point de Code et de Graphème Cluster peut être principalement passées sous silence parce que pour la plupart des langues modernes chaque "personnage" est mappé à un seul Point de Code (il y a dédié accentué les formes les plus couramment utilisées lettre+diacritique combinaisons). Encore, si vous vous aventurez dans des smileys, des drapeaux, etc... alors vous pourriez avoir à payer l'attention sur la distinction.


UTF Apprêt

Ensuite, une série de Points de Code Unicode doit être codé; la commune encodage est UTF-8, UTF-16 et UTF-32, les deux derniers existant dans les deux Little-Endian et Big-Endian formes, pour un total de 5 communes encodages.

En UTF-X, où X est la taille en bits de l' Unité de Code, chaque Point de Code est représenté par une ou plusieurs Unités de Code, en fonction de son ampleur:

  • UTF-8: 1 à 4 Unités de Code,
  • UTF-16: 1 ou 2 Unités de Code,
  • UTF-32: 1 Code Unité.

std::string et std::wstring.

  1. Ne pas utiliser std::wstring si vous vous souciez de la portabilité (wchar_t seulement 16 bits sur Windows); utilisation std::u32string à la place (aka std::basic_string<char32_t>).
  2. La représentation en mémoire (std::string ou std::wstring) est indépendant de la sur-disque représentation (UTF-8, UTF-16 ou UTF-32), alors préparez-vous pour avoir à les convertir à la limite (lecture et écriture).
  3. Alors qu'un 32 bits wchar_t assure qu'une Unité de Code représente le Point de Code, il n'est toujours pas représenter une complète Graphème Cluster.

Si vous êtes seulement de la lecture ou de la composition des chaînes, vous ne devriez pas avoir de problèmes peu avec std::string ou std::wstring.

Les ennuis commencent lorsque vous commencez à découper et couper en dés, alors vous devez faire attention à (1) du Code frontières (en UTF-8 ou UTF-16) et (2) de Graphème Clusters limites. L'ancien peut être traitée assez facilement sur votre propre, ce dernier nécessite l'utilisation de l'Unicode conscient de la bibliothèque.


La cueillette std::string ou std::u32string?

Si la performance est une préoccupation, il est probable qu' std::string ont un meilleur rendement en raison de sa plus petite empreinte mémoire; si un usage intensif de Chinois peut changer la donne. Comme toujours, profil.

Si Graphème les Clusters ne sont pas un problème, std::u32string a l'avantage de simplifier les choses: 1 Unité de Code -> 1 Point de Code signifie que vous ne pouvez pas accidentellement divisé Points de Code, et toutes les fonctions de l' std::basic_string travaillent hors de la boîte.

Si vous interface avec les logiciels prenant std::string ou char*/char const*, puis de s'y tenir std::string pour éviter les va-et-vient des conversions. Ça va être une douleur autrement.


UTF-8 std::string.

UTF-8 fonctionne très bien, en std::string.

La plupart des opérations de travail hors de la boîte, car le codage UTF-8 est l'auto-synchronisation et de l'arrière-compatible avec l'ASCII.

En raison de la façon dont les Points de Code sont codés à la recherche d'un Point de Code ne peut pas accidentellement match au milieu d'un autre Point de Code:

  • str.find('\n') de travaux,
  • str.find("...") travaux pour la mise en correspondance se fait octet par octet1,
  • str.find_first_of("\r\n") fonctionne si la recherche de caractères ASCII.

De même, regex doit surtout fonctionne hors de la boîte. Comme une séquence de caractères ("haha") est juste une séquence d'octets ("哈"), habitudes de recherche de base doit travailler hors de la boîte.

Méfiez-vous, cependant, de classes de caractères (comme [:alphanum:]), car selon l'expression rationnelle de la saveur et de la mise en œuvre, il peut ou peut ne pas correspondre à des caractères Unicode.

De même, méfiez-vous de l'application des répétiteurs pour les non-ASCII "caractères", "哈?" peut considérer que le dernier octet pour être facultatif; utiliser des parenthèses pour délimiter clairement la répétition de la séquence d'octets dans de tels cas: "(哈)?".

1Les concepts clés de recherche sont la normalisation et de classement; cela affecte toutes les opérations de comparaison. std::string toujours comparer (et donc de tri) octet par octet, sans égard pour la comparaison des règles spécifiques à une langue ou d'une utilisation. Si vous devez gérer la normalisation/classement, vous avez besoin d'un complet Unicode de la bibliothèque, tels que les unités de soins intensifs.

12voto

James Picone Points 988

std::string et les amis sont encodage agnostique. La seule différence entre std::wstring et std::string sont qu' std::wstring utilise wchar_t comme l'élément individuel, pas char. Pour la plupart des compilateurs de ce dernier est de 8 bits. Le premier est censé être suffisamment grande pour contenir tout caractère unicode, mais dans la pratique, sur certains systèmes, il n'est pas (Microsoft compilateur, par exemple, utilise un type 16 bits). Vous ne pouvez pas stocker UTF-8 std::wstring; ce n'est pas ce qu'il est conçu pour. Il est conçu pour être un équivalent de l'UTF-32 - une chaîne où chaque élément est un simple Unicode codepoint.

Si vous voulez de l'indice des chaînes UTF-8 par Unicode codepoint ou composé unicode glyphe (ou autre chose), le comte de la longueur d'une chaîne UTF-8 Unicode codepoints ou quelque autre objet unicode, ou trouver en Unicode codepoint, vous allez avoir besoin d'utiliser autre chose que de la bibliothèque standard. ICU est l'une des bibliothèques dans le domaine; il peut y en avoir d'autres.

Quelque chose qui est sans doute intéressant de noter, c'est que si vous êtes à la recherche pour les caractères ASCII, vous pouvez traiter la plupart UTF-8 bytestream comme si c'était octet par octet. Chaque caractère ASCII code pour le même en UTF-8 comme il le fait en ASCII, et tous les multi-octets unité en UTF-8 est la garantie de ne pas inclure tous les octets de la plage ASCII.

7voto

zneak Points 45458

Les deux std::string et std::wstring devez utiliser l'encodage UTF pour représenter les caractères Unicode. Sur macOS spécifiquement, std::string est UTF-8 (8 bits unités de code), et std::wstring est de l'UTF-32 (32 bits unités de code); à noter que la taille de l' wchar_t est dépend de la plateforme.

Pour les deux, size le suivi du nombre d'unités de code au lieu de le nombre de points de code, ou de graphème clusters. (Un point de code est un nommé Unicode entité, un ou plusieurs de ce qui forme un graphème cluster. Graphème grappes visibles sont les personnages dont les utilisateurs interagissent avec, comme des lettres ou des émoticônes.)

Bien que je ne suis pas familier avec la représentation Unicode de Chinois, il est très possible que lorsque vous utilisez UTF-32, le nombre d'unités de code est souvent très proche du nombre de graphème clusters. Bien entendu, cela se fait au prix de l'utilisation de jusqu'à 4 fois plus de mémoire.

Le plus précis solution serait d'utiliser une bibliothèque Unicode, comme les soins intensifs, pour calculer les propriétés Unicode que vous êtes après.

Enfin, UTF chaînes de caractères dans les langues humaines qui n'utilisent pas les caractères composés généralement de faire assez bien avec find/regex. Je ne suis pas sûr de Chinois, mais l'anglais est l'un d'entre eux.

5voto

FaTony Points 594

Envisagez de passer à la classe +20 et c'est la meilleure chose que nous ayons à partir `` de 2019 pour la tenue de l'UTF-8. Il n'y a pas d'installations de bibliothèque standard pour accéder à des points de code individuels ou des clusters de graphème, mais au moins votre type est assez fort pour au moins dis-le est vrai UTF-8.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X