2010-07-25 4 views
2

Est-il bon/sûr/possible d'utiliser la petite bibliothèque utfcpp pour convertir tout ce que je récupère de l'API Windows (FindFirstFileW et autres) en une représentation UTF8 valide en utilisant utf16to8?utfcpp et Win32 large API

Je voudrais utiliser l'UTF8 en interne, mais j'ai du mal à obtenir la bonne sortie (via wcout après une autre conversion ou un simple cout). Les caractères ASCII normaux fonctionnent bien sûr, mais ñä se fane.

Ou est-il une alternative plus facile?

Merci! MISE À JOUR: Merci à Hans (ci-dessous), j'ai maintenant une conversion UTF8 facile UTF8 < -> UTF16 via l'API Windows. La conversion bidirectionnelle fonctionne, mais la chaîne UTF8 de UTF16 a quelques caractères supplémentaires qui pourraient me causer quelques problèmes plus tard ...). Je vais partager ici par pure convivialité :)):

// UTF16 -> UTF8 conversion 
std::string toUTF8(const std::wstring &input) 
{ 
    // get length 
    int length = WideCharToMultiByte(CP_UTF8, NULL, 
             input.c_str(), input.size(), 
             NULL, 0, 
             NULL, NULL); 
    if(!(length > 0)) 
     return std::string(); 
    else 
    { 
     std::string result; 
     result.resize(length); 

     if(WideCharToMultiByte(CP_UTF8, NULL, 
           input.c_str(), input.size(), 
           &result[0], result.size(), 
           NULL, NULL) > 0) 
      return result; 
     else 
      throw std::runtime_error("Failure to execute toUTF8: conversion failed."); 
    } 
} 
// UTF8 -> UTF16 conversion 
std::wstring toUTF16(const std::string &input) 
{ 
    // get length 
    int length = MultiByteToWideChar(CP_UTF8, NULL, 
             input.c_str(), input.size(), 
             NULL, 0); 
    if(!(length > 0)) 
     return std::wstring(); 
    else 
    { 
     std::wstring result; 
     result.resize(length); 

     if(MultiByteToWideChar(CP_UTF8, NULL, 
           input.c_str(), input.size(), 
           &result[0], result.size()) > 0) 
      return result; 
     else 
      throw std::runtime_error("Failure to execute toUTF16: conversion failed."); 
    } 
} 

Répondre

7

L'API Win32 dispose déjà d'une fonction pour ce faire, WideCharToMultiByte() avec CodePage = CP_UTF8. Vous évite d'avoir à compter sur une autre bibliothèque.

Vous ne pouvez normalement pas utiliser le résultat avec wcout. Sa sortie va à la console, elle utilise un encodage OEM 8 bits pour des raisons héritées. Vous pouvez changer la page de code avec SetConsoleCP(), 65001 est la page de code pour UTF-8 (CP_UTF8).

Votre prochaine pierre d'achoppement sera la police utilisée pour la console. Vous devrez le changer, mais trouver une police avec un pas fixe et un jeu complet de glyphes pour couvrir Unicode sera difficile. Vous verrez que vous avez un problème de police lorsque vous obtenez des rectangles carrés dans la sortie. Les points d'interrogation sont des problèmes d'encodage.

+0

Juste pour clarifier: une police (au moins une police TT) vous permet de spécifier quel glyphe sera affiché pour un codepoint pour lequel la police ne contient pas de glyphe. C'est * généralement * un rectangle vide, mais pourrait être essentiellement tout ce que le concepteur de police a choisi. –

+0

Je pensais que ceux-ci étaient disponibles, mais je ne savais pas qu'ils étaient pour la conversion UTF-8 -> UTF-16 (je pensais bêtement qu'ils utilisaient l'encodage UCS-2 à la place). La sortie de console est en effet une chose en désordre. Peut-être que je peux sortir l'UTF-8 dans un fichier et l'ouvrir avec, disons Notepad ++ (c'est seulement pour vérifier ce que fait mon programme)? – rubenvb

+0

Bien sûr, devrait fonctionner. Tant que vous pouvez le convaincre qu'il s'agit d'un fichier UTF-8, il nécessite normalement une nomenclature au début du fichier. Ecrivez 0xef 0xbb 0xbf d'abord pour être sûr. –

3

Pourquoi voulez-vous utiliser UTF8 en interne? Travaillez-vous avec autant de texte que l'utilisation de UTF16 créerait des demandes de mémoire déraisonnables? Même si c'était le cas, vous feriez mieux d'utiliser des caractères larges de toute façon et de traiter les problèmes de mémoire d'une autre manière (en utilisant un cache disque, de meilleurs algorithmes ou structures de données). Votre code sera beaucoup plus propre et plus facile à gérer en utilisant des caractères larges natifs de l'API Win32 en interne, et en faisant uniquement des conversions UTF8 lors de la lecture ou de l'écriture des données qui le nécessitent (par exemple fichiers XML ou API REST).

Votre problème peut également se produire au moment où vous imprimez votre sortie sur la console, voir: Output unicode strings in Windows console app

Enfin je n'ai pas utilisé la bibliothèque utfcpp, mais les conversions UTF8 sont assez trivial à effectuer à l'aide WideCharToMultiByte Win32 et MultiByteToWideChar avec CP_UTF8 comme page de code. Personnellement, je ferais une conversion en une fois et travaillerais avec le texte en UTF16 jusqu'à ce qu'il soit temps de le sortir ou de le transférer en UTF8 si nécessaire.

+2

Notez que les caractères larges sous Windows sont en 16 bits et doivent donc être codés en UTF-16. Cela aussi est un codage multi-octets. Même si vous êtes probablement moins susceptible de rencontrer des points de code Unicode nécessitant deux octets de 16 bits à coder, ceux-ci existent, et vous ne pouvez pas supposer que chaque valeur de 16 bits est un caractère individuel. – sbi

+1

True, le principal avantage est que UTF16 est le codage natif pour Windows, et travailler avec cela signifie ne pas avoir à convertir continuellement vers et à partir d'un autre codage lors de l'appel API. –

+1

Je suis en train de développer une application multiplateforme, et sur linux wchar_t, le double de ce qu'ils sont sous Windows.J'ai besoin de l'API win32 pour les noms de fichiers, tout le reste est du texte brut (caractères ASCII seulement). Je ne vois pas de raison de traiter le double de la quantité d'octets, quand une simple chaîne std :: suffira. – rubenvb