string
? wstring
?
std::string
est un basic_string
modélisé sur un char
et std::wstring
sur un wchar_t
.
char
vs. wchar_t
char
est censé contenir un caractère, généralement un caractère d'un octet. wchar_t
est censé contenir un caractère large, et ensuite, les choses se compliquent : sous Linux, un caractère de type wchar_t
est de 4 octets, alors que sous Windows, c'est 2-bytes
qu'en est-il Unicode alors ?
Le problème est que ni l'un ni l'autre char
ni wchar_t
est directement liée à l'unicode.
Sous Linux ?
Prenons un système d'exploitation Linux : Mon système Ubuntu est déjà conscient de l'unicode. Lorsque je travaille avec une chaîne de caractères, elle est codée nativement en format UTF-8 (c'est-à-dire une chaîne de caractères Unicode). Le code suivant :
#include <cstring>
#include <iostream>
int main(int argc, char* argv[])
{
const char text[] = "olé" ;
const wchar_t wtext[] = L"olé" ;
std::cout << "sizeof(char) : " << sizeof(char) << std::endl ;
std::cout << "text : " << text << std::endl ;
std::cout << "sizeof(text) : " << sizeof(text) << std::endl ;
std::cout << "strlen(text) : " << strlen(text) << std::endl ;
std::cout << "text(binary) :" ;
for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
{
std::cout << " " << static_cast<unsigned int>(static_cast<unsigned char>(text[i])) ;
}
std::cout << std::endl << std::endl ;
std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
//std::cout << "wtext : " << wtext << std::endl ; <- error
std::cout << "wtext : UNABLE TO CONVERT NATIVELY." << std::endl ;
std::wcout << L"wtext : " << wtext << std::endl;
std::cout << "sizeof(wtext) : " << sizeof(wtext) << std::endl ;
std::cout << "wcslen(wtext) : " << wcslen(wtext) << std::endl ;
std::cout << "wtext(binary) :" ;
for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
{
std::cout << " " << static_cast<unsigned int>(static_cast<unsigned short>(wtext[i])) ;
}
std::cout << std::endl << std::endl ;
return 0;
}
sort le texte suivant :
sizeof(char) : 1
text : olé
sizeof(text) : 5
strlen(text) : 4
text(binary) : 111 108 195 169
sizeof(wchar_t) : 4
wtext : UNABLE TO CONVERT NATIVELY.
wtext : ol�
sizeof(wtext) : 16
wcslen(wtext) : 3
wtext(binary) : 111 108 233
Vous verrez le texte "olé" en char
est en réalité construit par quatre caractères : 110, 108, 195 et 169 (sans compter le zéro de queue). (Je vous laisse étudier le wchar_t
à titre d'exercice)
Ainsi, lorsque vous travaillez avec un caractère sous Linux, vous finissez généralement par utiliser Unicode sans même le savoir. Et comme std::string fonctionne avec les chars, std::string est donc déjà prêt pour l'unicode.
Notez que std::string, comme l'API des chaînes de caractères en C, considérera la chaîne "olé" comme ayant 4 caractères, et non trois. Vous devez donc être prudent lorsque vous tronquez/jouez avec des caractères unicodes car certaines combinaisons de caractères sont interdites en UTF-8.
Sous Windows ?
Sous Windows, c'est un peu différent. Win32 a dû prendre en charge un grand nombre d'applications fonctionnant avec char
et sur différents charsets / codepages produit dans le monde entier, avant l'avènement d'Unicode.
Leur solution était donc intéressante : Si une application fonctionne avec char
Les chaînes de caractères sont alors codées/imprimées/affichées sur les étiquettes de l'interface graphique en utilisant le jeu de caractères/codepage local de la machine. Par exemple, "olé" sera "olé" dans un Windows localisé en français, mais sera quelque chose de différent dans un Windows localisé en cyrillique ("olй" si vous utilisez le codage de la langue française). Windows-1251 ). Ainsi, les "applications historiques" fonctionnent généralement toujours de la même manière.
Pour les applications basées sur Unicode, Windows utilise wchar_t
qui a une largeur de 2 octets et qui est codée dans le format UTF-16 Unicode, qui est codé sur des caractères de 2 octets (ou au moins, le plus compatible UCS-2, qui est presque la même chose IIRC).
Applications utilisant char
sont dits "multi-octets" (parce que chaque glyphe est composé d'un ou de plusieurs char
), tandis que les applications utilisant wchar_t
sont dites "larges" (parce que chaque glyphe est composé d'un ou de deux wchar_t
. Voir MultiByteToWideChar et WideCharToMultiByte API de conversion Win32 pour plus d'informations.
Ainsi, si vous travaillez sous Windows, vous veulent absolument à utiliser wchar_t
(à moins que vous n'utilisiez un cadre cachant cela, comme GTK+ ou QT ...). Le fait est que, dans les coulisses, Windows travaille avec les éléments suivants wchar_t
chaînes de caractères, de sorte que même les applications historiques auront leurs char
chaînes de caractères converties en wchar_t
lors de l'utilisation d'API telles que SetWindowText (fonction API de bas niveau permettant de définir l'étiquette sur une interface graphique Win32).
Des problèmes de mémoire ?
UTF-32 est de 4 octets par caractères, il n'y a donc pas grand chose à ajouter, si ce n'est qu'un texte UTF-8 et un texte UTF-16 utiliseront toujours moins ou la même quantité de mémoire qu'un texte UTF-32 (et généralement moins).
S'il y a un problème de mémoire, sachez que pour la plupart des langues occidentales, le texte UTF-8 utilisera moins de mémoire que le même texte UTF-16.
Cependant, pour les autres langues (chinois, japonais, etc.), la mémoire utilisée sera soit la même, soit plus grande pour UTF-8 que pour UTF-16.
En somme, l'UTF-16 utilisera le plus souvent 2 octets par caractères (à moins que vous n'ayez affaire à des glyphes d'une langue ésotérique (klingon ? elfique ?), tandis que l'UTF-8 dépensera de 1 à 4 octets.
Voir http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 pour plus d'informations.
Conclusion
1. Quand dois-je utiliser std::wstring plutôt que std::string ?
Sur Linux ? Presque jamais (§).
Sur Windows ? Presque toujours (§).
Sur le code multiplateforme ? Cela dépend de votre boîte à outils...
(§) : sauf si vous utilisez un toolkit/framework disant le contraire
2. std::string peut-il contenir tout le jeu de caractères ASCII, y compris les caractères spéciaux ?
Avis : Une std::string convient pour contenir un tampon 'binaire', alors qu'une std::wstring ne convient pas !
Sur Linux ? Oui.
Sous Windows ? Seuls les caractères spéciaux disponibles pour la locale actuelle de l'utilisateur de Windows.
Edit (Après un commentaire de Johann Gerell ) : un std::string sera suffisant pour gérer toutes les chaînes de caractères (chaque caractère étant un nombre de 0 à 255). Mais :
- L'ASCII est censé aller de 0 à 127. Les caractères supérieurs ne sont PAS ASCII.
- un caractère de 0 à 127 sera maintenu correctement
- un caractère de 128 à 255 aura une signification qui dépend de votre encodage (unicode, non-unicode, etc.), mais il pourra contenir tous les glyphes Unicode tant qu'ils sont encodés en UTF-8.
3. std::wstring est-il supporté par presque tous les compilateurs C++ courants ?
La plupart du temps, à l'exception des compilateurs basés sur GCC qui sont portés sur Windows.
Cela fonctionne sur mon g++ 4.3.2 (sous Linux), et j'ai utilisé l'API Unicode sur Win32 depuis Visual C++ 6.
4. Qu'est-ce qu'un caractère large ?
En C/C++, c'est un type de caractère qui s'écrit wchar_t
qui est plus grande que la simple char
type de caractère. Il est censé être utilisé pour mettre à l'intérieur des caractères dont les indices (comme les glyphes Unicode) sont supérieurs à 255 (ou 127, selon...)