69 votes

Comment créer un littéral de chaîne UTF-8 en Visual C++ 2008

En VC++ 2003, je pouvais simplement enregistrer le fichier source en UTF-8 et toutes les chaînes étaient utilisées telles quelles. En d'autres termes, le code suivant imprimerait les chaînes telles quelles sur la console. Si le fichier source était enregistré en UTF-8, la sortie serait UTF-8.

printf("Chinese (Traditional)");
printf(" ()");
printf(" ()");
printf("Chinês (Tradicional)");

J'ai enregistré le fichier au format UTF-8 avec la nomenclature UTF-8. Cependant, la compilation avec VC2008 donne le résultat suivant :

warning C4566: character represented by universal-character-name '\uC911' 
cannot be represented in the current code page (932)
warning C4566: character represented by universal-character-name '\uAD6D' 
cannot be represented in the current code page (932)
etc.

Les personnages à l'origine de ces avertissements sont corrompus. Ceux qui correspondent à la locale (dans ce cas, 932 = japonais) sont convertis dans l'encodage de la locale, c'est-à-dire Shift-JIS.

Je n'arrive pas à trouver le moyen de faire compiler ce fichier par VC++ 2008. Notez que la locale que j'utilise dans le fichier source n'a pas d'importance. Il ne semble pas y avoir de locale qui dise "Je sais ce que je fais, alors ne changez pas mes chaînes de caractères". En particulier, l'inutile pseudo-locale UTF-8 ne fonctionne pas.

#pragma setlocale(".65001") 
=> error C2175: '.65001' : invalid locale

Le "C" non plus :

#pragma setlocale("C") 
=> see warnings above (in particular locale is still 932)

Il semble que VC2008 force tous les caractères dans la locale spécifiée (ou par défaut), et cette locale ne peut pas être UTF-8. Je ne veux pas modifier le fichier pour utiliser des chaînes d'échappement comme " \xbf\x11... "parce que la même source est compilée avec gcc qui peut très bien traiter les fichiers UTF-8.

Existe-t-il un moyen de spécifier que la compilation du fichier source ne doit pas toucher aux chaînes de caractères ?

Pour poser la question différemment, quels drapeaux de compilation puis-je utiliser pour spécifier la compatibilité rétroactive avec VC2003 lors de la compilation du fichier source, c'est-à-dire ne pas modifier les chaînes de caractères, les utiliser telles quelles, octet par octet.

Mise à jour

Merci pour les suggestions, mais je veux éviter les wchar. Étant donné que cette application traite exclusivement des chaînes de caractères en UTF-8, l'utilisation de wchar m'obligerait à reconvertir toutes les chaînes de caractères en UTF-8, ce qui ne devrait pas être nécessaire. Toutes les entrées, sorties et traitements internes sont en UTF-8. C'est une application simple qui fonctionne bien telle quelle sous Linux et lorsqu'elle est compilée avec VC2003. Je voudrais pouvoir compiler la même application avec VC2008 et qu'elle fonctionne.

Pour que cela soit possible, il faut que VC2008 n'essaie pas de le convertir dans la locale de ma machine locale (japonais, 932). Je veux que VC2008 soit rétrocompatible avec VC2003. Je veux une locale ou un paramètre de compilation qui dit que les chaînes de caractères sont utilisées telles quelles, essentiellement comme des tableaux opaques de chars, ou en UTF-8. Il semble que je sois coincé avec VC2003 et gcc, VC2008 essayant d'être trop intelligent dans ce cas.

1 votes

Voir une réponse pour les versions plus récentes de VS : stackoverflow.com/questions/19987448/

0 votes

Je viens de rencontrer ce même problème dans VS 2012. Il bousille mon encodage UTF-8. Pourquoi VS est-il si cassé ? Je pense que je vais juste mettre mes données dans un fichier externe où elles ne seront pas gâchées. Avoir besoin de travailler autour d'un compilateur bogué est assez ennuyeux. BTW, C++11 permet à l'encodage d'être spécifié comme u8 "literal", mais VS 2012 ne supporte pas cela, donc n'est d'aucune aide.

33voto

brofield Points 1164

Mise à jour :

J'ai décidé qu'il n'y a pas de moyen garanti de le faire. La solution que je présente ci-dessous fonctionne pour la version anglaise de VC2003, mais échoue lors de la compilation avec la version japonaise de VC2003 (ou peut-être est-ce l'OS japonais). Dans tous les cas, on ne peut pas compter sur elle pour fonctionner. Notez que même le fait de déclarer tout comme des chaînes L"" n'a pas fonctionné (et est douloureux dans gcc comme décrit ci-dessous).

Au lieu de cela, je pense qu'il faut simplement prendre le taureau par les cornes et déplacer tout le texte dans un fichier de données et le charger à partir de là. Je stocke maintenant le texte dans des fichiers INI et j'y accède par l'intermédiaire de SimpleIni (bibliothèque de fichiers INI multiplateforme). Au moins, il y a une garantie que cela fonctionne puisque tout le texte est hors du programme.

Original :

Je réponds moi-même à cette question puisque seul Evan semble comprendre le problème. Les réponses concernant ce qu'est Unicode et comment utiliser wchar_t ne sont pas pertinentes pour ce problème car il ne s'agit pas d'internationalisation, ni d'une mauvaise compréhension d'Unicode, des encodages de caractères. J'apprécie votre tentative d'aide, mais je m'excuse si je n'ai pas été assez clair.

Le problème est que j'ai des fichiers sources qui doivent faire l'objet d'une compilation croisée sous une variété de plateformes et de compilateurs. Le programme effectue un traitement UTF-8. Il ne se soucie pas des autres encodages. Je veux avoir des chaînes littérales en UTF-8 comme cela fonctionne actuellement avec gcc et vc2003. Comment puis-je le faire avec VC2008 ? (c'est-à-dire une solution rétrocompatible).

Voici ce que j'ai trouvé :

gcc (v4.3.2 20081105) :

  • Les chaînes littérales sont utilisées telles quelles (chaînes brutes).
  • supporte les fichiers sources encodés en UTF-8
  • les fichiers sources ne doivent pas avoir de BOM UTF-8

vc2003 :

  • Les chaînes littérales sont utilisées telles quelles (chaînes brutes).
  • supporte les fichiers sources encodés en UTF-8
  • les fichiers sources peuvent avoir ou non un BOM UTF-8 (cela n'a pas d'importance)

vc2005+ :

  • Les chaînes de caractères sont traitées par le compilateur (pas de chaînes brutes).
  • Les chaînes de caractères sont ré-encodées dans la locale spécifiée.
  • UTF-8 n'est pas supporté comme locale cible
  • les fichiers source doivent avoir une nomenclature UTF-8

Ainsi, la réponse simple est que, pour cet objectif particulier, VC2005+ est cassé et ne fournit pas de chemin de compilation rétrocompatible. La seule façon d'obtenir des chaînes Unicode dans le programme compilé est via UTF-8 + BOM + wchar, ce qui signifie que je dois reconvertir toutes les chaînes en UTF-8 au moment de l'utilisation.

Il n'existe pas de méthode simple et multiplateforme pour convertir un wchar en UTF-8. Par exemple, quelle est la taille et l'encodage du wchar ? Sous Windows, UTF-16. Sur d'autres plateformes ? Cela varie. Voir le Projet ICU pour plus de détails.

Finalement, j'ai décidé d'éviter le coût de conversion sur tous les compilateurs autres que vc2005+ avec des sources comme les suivantes.

#if defined(_MSC_VER) && _MSC_VER > 1310
// Visual C++ 2005 and later require the source files in UTF-8, and all strings 
// to be encoded as wchar_t otherwise the strings will be converted into the 
// local multibyte encoding and cause errors. To use a wchar_t as UTF-8, these 
// strings then need to be convert back to UTF-8. This function is just a rough 
// example of how to do this.
# define utf8(str)  ConvertToUTF8(L##str)
const char * ConvertToUTF8(const wchar_t * pStr) {
    static char szBuf[1024];
    WideCharToMultiByte(CP_UTF8, 0, pStr, -1, szBuf, sizeof(szBuf), NULL, NULL);
    return szBuf;
}
#else
// Visual C++ 2003 and gcc will use the string literals as is, so the files 
// should be saved as UTF-8. gcc requires the files to not have a UTF-8 BOM.
# define utf8(str)  str
#endif

Notez que ce code n'est qu'un exemple simplifié. Une utilisation en production nécessiterait de le nettoyer de diverses manières (sécurité des fils, vérification des erreurs, vérification de la taille des tampons, etc.)

Ceci est utilisé comme le code suivant. Il compile proprement et fonctionne correctement dans mes tests sur gcc, vc2003, et vc2008 :

std::string mText;
mText = utf8("Chinese (Traditional)");
mText = utf8("中国語 (繁体)");
mText = utf8("중국어 (번체)");
mText = utf8("Chinês (Tradicional)");

0 votes

Excellente réponse et solution, merci.

2 votes

Temps d'exécution la conversion, tu te moques de moi ? Fais défiler en dessous de pour une solution beaucoup plus simple, au moment de la compilation (il suffit de sauvegarder votre code source en UTF-8).

17voto

Evan Teran Points 42370

Il est probablement préférable d'utiliser des chaînes de caractères larges et de les convertir en UTF-8 si nécessaire. Je pense que votre meilleure chance est, comme vous l'avez mentionné, d'utiliser des échappements hexagonaux dans les chaînes de caractères. Supposons que vous vouliez le point de code \uC911 vous pouvez faire ça.

const char *str = "\xEC\xA4\x91";

Je pense que cela fonctionnera très bien, mais ce n'est pas très lisible, donc si vous faites cela, veuillez le commenter pour expliquer.

2 votes

+1, mais je préférerais les échappatoires octales à trois chiffres plutôt que les échappatoires hexagonales, en raison de la règle de la goinfrerie maximale des échappatoires hexagonales. Exemple "Ond bråd död i Venedig" en oct : "Ond bra\314\212d do\314\210d i Venedig" en hexagone : "Ond bra\xCC\x8A""d do\xCC\x88""d i Venedig" .

0 votes

Ce n'est pas la représentation de \uC911 en UTF-8.

0 votes

@Lev : c'est juste, je fixe.

16voto

Brofield,

J'ai eu exactement le même problème et je viens de tomber sur une solution qui ne nécessite pas de convertir vos chaînes de caractères en caractères larges et inversement : enregistrez votre fichier source en UTF-8. sans et VC2008 le laissera tranquille. Cela a bien fonctionné quand j'ai compris qu'il fallait laisser tomber la signature. Pour résumer :

Unicode (UTF-8 sans signature) - Codepage 65001, n'affiche pas l'avertissement c4566 dans VC2008 et n'oblige pas VC à modifier l'encodage, alors que le Codepage 65001 (UTF-8 avec signature) affiche c4566 (comme vous l'avez constaté).

J'espère que ce n'est pas trop tard pour vous aider, mais cela pourrait accélérer votre application VC2008 de supprimer votre solution de rechange.

1 votes

-1, N'a pas fonctionné pour moi (anglais VS2010). J'obtiens l'avertissement C4819 et de nombreuses autres erreurs de syntaxe. Je travaille cependant en Corée et mon système d'exploitation est configuré pour afficher les chaînes unicode en coréen.

0 votes

Quelque chose comme std::string jp1 = "" ; fonctionne avec VS2012 si le fichier est enregistré "Unicode (UTF-8 sans signature) - Codepage 65001".

0 votes

Cette solution fonctionne pour moi avec les caractères Hangul en anglais VS2015.

14voto

Vladius Points 68

Fichier/Options avancées d'enregistrement/Encodage : "Unicode (UTF-8 sans signature ) - Codepage 65001"

3 votes

Essayez de compiler avec une version japonaise du compilateur.

3 votes

Vous dites que cela ne fonctionne pas "sans signature". C'est sûrement très étrange, puisque le compilateur ne reconnaîtrait pas l'entrée comme une entrée UTF-8 sans effectuer de traitement supplémentaire. Vous dites que la version japonaise effectue une telle logique ; très intéressant. L'astuce fonctionne néanmoins pour le russe.

1 votes

Cette astuce devrait évidemment fonctionner avec tout encodage qui laisse la partie ASCII intacte. C'est-à-dire UTF-8, ISO-8859-x, KOI8-R et bien d'autres.

10voto

Henrik Haftmann Points 77

Le comportement standard de Visual C++ (2005+) COMPILER pour les fichiers sources est le suivant :

  • CP1252 (pour cet exemple, page de codes de l'Europe occidentale) :
    • "Ä"C4 00
    • 'Ä'C4
    • L"Ä"00C4 0000
    • L'Ä'00C4
  • UTF-8 sans BOM :
    • "Ä"C3 84 00 (= UTF-8)
    • 'Ä' → avertissement : constante à plusieurs caractères
    • "Ω"E2 84 A6 00 (= UTF-8, comme prévu)
    • L"A"00C3 0084 0000 (faux !)
    • L'Ä' → avertissement : constante à plusieurs caractères
    • L"Ω"00E2 0084 00A6 0000 (faux !)
  • UTF-8 avec BOM :
    • "Ä"C4 00 (= CP1252, plus d'UTF-8),
    • 'Ä'C4
    • "Ω" → erreur : ne peut pas convertir en CP1252 !
    • L"Ä"00C4 0000 (correct)
    • L'Ä'00C4
    • L"Ω"2126 0000 (correct)

Vous voyez, le compilateur C traite les fichiers UTF-8 sans BOM de la même manière que le CP1252. Par conséquent, il est impossible pour le compilateur de mélanger les chaînes UTF-8 et UTF-16 dans la sortie compilée ! Vous devez donc décider pour un seul fichier de code source :

  • o bien utiliser UTF-8 avec BOM et générer uniquement des chaînes UTF-16 (c'est-à-dire toujours utiliser L préfixe),
  • o UTF-8 sans BOM et générer des chaînes UTF-8 uniquement (c'est-à-dire ne jamais utiliser L préfixe).
  • Les caractères ASCII 7 bits ne sont pas concernés et peuvent être utilisés avec ou sans L préfixe

Indépendamment, l'ÉDITEUR peut détecter automatiquement les fichiers UTF-8 sans BOM comme des fichiers 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