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.

8voto

Alexander Jung Points 21

D'un commentaire sur ce très beau blog
"Utiliser UTF-8 comme représentation interne des chaînes de caractères en C et C++ avec Visual Studio"
\=> http://www.nubaria.com/en/blog/?p=289

#pragma execution_character_set("utf-8") 

Il nécessite Visual Studio 2008 SP1, et le correctif suivant :

http://support.microsoft.com/kb/980263 ....

1 votes

+1, bien que cela ne soit apparemment pas pris en charge actuellement dans VS2012 : connect.microsoft.com/VisualStudio/feedback/details/773186/

4voto

Martin Liversage Points 43712

Que pensez-vous de ça ? Vous stockez les chaînes de caractères dans un fichier encodé en UTF-8, puis vous les pré-traitez dans un fichier source C++ encodé en ASCII. Vous conservez l'encodage UTF-8 à l'intérieur de la chaîne en utilisant des échappatoires hexadécimaux. La chaîne

"中国語 (繁体)"

est converti en

"\xE4\xB8\xAD\xE5\x9B\xBD\xE8\xAA\x9E (\xE7\xB9\x81\xE4\xBD\x93)"

Bien sûr, c'est illisible pour tout humain, et le but est juste d'éviter les problèmes avec le compilateur.

Vous pouvez soit utiliser le préprocesseur C++ pour référencer les chaînes dans le fichier d'en-tête converti, soit convertir l'ensemble de votre source UTF-8 en ASCII avant la compilation en utilisant cette astuce.

3voto

Michael J Points 4112

Une conversion portable à partir de n'importe quel encodage natif que vous avez est simple en utilisant char_traits::widen().

#include <locale>
#include <string>
#include <vector>

/////////////////////////////////////////////////////////
// NativeToUtf16 - Convert a string from the native 
//                 encoding to Unicode UTF-16
// Parameters:
//   sNative (in): Input String
// Returns:        Converted string
/////////////////////////////////////////////////////////
std::wstring NativeToUtf16(const std::string &sNative)
{
  std::locale locNative;

  // The UTF-16 will never be longer than the input string
  std::vector<wchar_t> vUtf16(1+sNative.length());

  // convert
  std::use_facet< std::ctype<wchar_t> >(locNative).widen(
        sNative.c_str(), 
        sNative.c_str()+sNative.length(), 
        &vUtf16[0]);

  return std::wstring(vUtf16.begin(), vUtf16.end());
}

En théorie, le voyage de retour, de UTF-16 à UTF-8, devrait être aussi facile, mais j'ai constaté que les locales UTF-8 ne fonctionnent pas correctement sur mon système (VC10 Express sur Win7).

J'ai donc écrit un convertisseur simple basé sur la RFC 3629.

/////////////////////////////////////////////////////////
// Utf16ToUtf8 -   Convert a character from UTF-16 
//                 encoding to UTF-8.
//                 NB: Does not handle Surrogate pairs.
//                     Does not test for badly formed 
//                     UTF-16
// Parameters:
//   chUtf16 (in): Input char
// Returns:        UTF-8 version as a string
/////////////////////////////////////////////////////////
std::string Utf16ToUtf8(wchar_t chUtf16)
{
    // From RFC 3629
    // 0000 0000-0000 007F   0xxxxxxx
    // 0000 0080-0000 07FF   110xxxxx 10xxxxxx
    // 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx

    // max output length is 3 bytes (plus one for Nul)
    unsigned char szUtf8[4] = "";

    if (chUtf16 < 0x80)
    {
        szUtf8[0] = static_cast<unsigned char>(chUtf16);
    }
    else if (chUtf16 < 0x7FF)
    {
        szUtf8[0] = static_cast<unsigned char>(0xC0 | ((chUtf16>>6)&0x1F));
        szUtf8[1] = static_cast<unsigned char>(0x80 | (chUtf16&0x3F));
    }
    else
    {
        szUtf8[0] = static_cast<unsigned char>(0xE0 | ((chUtf16>>12)&0xF));
        szUtf8[1] = static_cast<unsigned char>(0x80 | ((chUtf16>>6)&0x3F));
        szUtf8[2] = static_cast<unsigned char>(0x80 | (chUtf16&0x3F));
    }

    return reinterpret_cast<char *>(szUtf8);
}

/////////////////////////////////////////////////////////
// Utf16ToUtf8 -   Convert a string from UTF-16 encoding
//                 to UTF-8
// Parameters:
//   sNative (in): Input String
// Returns:        Converted string
/////////////////////////////////////////////////////////
std::string Utf16ToUtf8(const std::wstring &sUtf16)
{
    std::string sUtf8;
    std::wstring::const_iterator itr;

    for (itr=sUtf16.begin(); itr!=sUtf16.end(); ++itr)
        sUtf8 += Utf16ToUtf8(*itr);
    return sUtf8;
}

Je pense que cela devrait fonctionner sur n'importe quelle plateforme, mais je n'ai pas été en mesure de le tester, sauf sur mon propre système, et il se peut donc qu'il y ait des bogues.

#include <iostream>
#include <fstream>

int main()
{
    const char szTest[] = "Das tausendschöne Jungfräulein,\n"
                          "Das tausendschöne Herzelein,\n"
                          "Wollte Gott, wollte Gott,\n"
                          "ich wär' heute bei ihr!\n";

    std::wstring sUtf16 = NativeToUtf16(szTest);
    std::string  sUtf8  = Utf16ToUtf8(sUtf16);

    std::ofstream ofs("test.txt");
    if (ofs)
        ofs << sUtf8;
    return 0;
}

0 votes

Vous n'avez pas compris l'essentiel de la question. L'exigence était de ne jamais quitter UTF-8 de la compilation au traitement. En outre, Windows ne prend pas en charge UTF-8 comme page de code (en raison de l'hypothèse selon laquelle tous les MBCS ont un maximum de 2 octets). Voir blogs.msdn.com/b/michkap/archive/2007/01/03/1392379.aspx

0 votes

Ce n'est pas UTF-16, c'est UCS-2.

1voto

Windows programmer Points 5365

Essayez peut-être une expérience :

#pragma setlocale(".UTF-8")

o:

#pragma setlocale("english_england.UTF-8")

1voto

Daniel N. Points 11

J'ai eu un problème similaire. Mes chaînes de caractères UTF-8 ont été converties dans la page de code du système actuel pendant la compilation - j'ai simplement ouvert les fichiers .obj dans un visualisateur hexa et ils étaient déjà déformés. Par exemple, le caractère ć était juste un octet.

La solution pour moi a été d'enregistrer en UTF-8 et SANS BOM. C'est ainsi que j'ai trompé le compilateur. Il pense maintenant que c'est juste une source normale, et ne traduit pas les chaînes de caractères. Dans les fichiers .obj ć est maintenant de deux octets.

Ne tenez pas compte de certains commentateurs, s'il vous plaît. Je comprends ce que vous voulez - je veux la même chose : Source UTF-8, fichiers générés UTF-8, fichiers d'entrée UTF-8, UTF-8 sur les lignes de communication sans jamais traduire.

Peut-être que cela aide...

0 votes

C'est bien que ça marche pour vous. Je crois qu'il y a des problèmes si vous utilisez une locale système non anglaise. J'ai un compilateur japonais et une locale système japonaise, et cela n'a pas fonctionné pour moi car il semblait essayer de convertir les littéraux de chaîne de Shift-JIS, ce qui a échoué parce qu'ils étaient 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