2010-07-12 10 views
2

J'écris une couche wrapper à utiliser avec mingw qui fournit à l'application un environnement virtuel UTF-8. Les fonctions qui traitent des noms de fichiers sont des wrappers qui convertissent à partir de UTF-8 et appellent les fonctions "_w" correspondantes, et ainsi de suite. Le gros problème que j'ai rencontré est que Windows 'wchar_t est 16 bits.Comment traiter au mieux la laideur de wchar_t 16 bits de Windows?

Pour les opérations de système de fichiers, ce n'est pas grave. Je peux juste convertir d'avant en arrière entre UTF-8 et UTF-16, et tout va fonctionner. Mais l'API de conversion de caractères multi-octets/caractères C standard n'autorise pas les caractères multi-wchar_t.

solutions possibles:

  1. Fournir un environnement CESU-8 au lieu de UTF-8. Je n'aime vraiment pas celui-ci.
  2. Prenez la sortie facile et ne supporte que le BMP. Traiter les séquences UTF-8 de longueur 4 comme non valides.
  3. Extension de l'enveloppe pour remplacer wchar_t deet traiter WCHAR et wchar_t étant différent. C'est un problème, mais il peut être idéal pour le portage d'applications qui exigent un environnement propre de type POSIX et n'utilise pas wchar_t à des fins d'API Windows.
  4. L'entaille suivante:

mbrtowc délivre un wchar_t correspondant à la porteuse haute après avoir lu les 3 premiers octets d'un caractère UTF-8 de 4 octets, et maintient l'état restant dans l'objet mbstate_t. Lors de la réception de l'octet suivant, il le combine avec l'état enregistré pour sortir le substitut faible. Si le dernier octet finit par être invalide, il renvoie -1 (avec EILSEQ) et un seul substitut se retrouve dans le flux de sortie (mauvais ...).

wcrtomb génère les 2 premiers octets de UTF-8 lorsqu'il traite le substitut haut, et enregistre l'état restant dans son objet mbstate_t. Quand il traite ensuite le substitut bas, il combine cela avec l'état sauvegardé pour sortir les 2 derniers octets de UTF-8. Si un substitut bas valide n'est pas reçu, il renvoie -1 (avec EILSEQ) et une séquence UTF-8 incomplète finit dans le flux de sortie (mauvais ...). Le côté positif de ce hack est qu'il fonctionne tant que l'entrée est valide, et permet d'accéder à n'importe quel caractère UTF-8 et donc à tout nom de fichier/argument/etc. texte avec lequel l'application pourrait devoir travailler. Les inconvénients sont qu'il n'est pas strictement conforme à ISO C (wchar_t chaîne n'est pas autorisé à être à état) et qu'il retarde la détection des caractères malformés jusqu'à ce que la sortie partielle incorrecte a déjà été écrite. Je suis à la recherche de commentaires sur les différentes options, et en particulier mon hack proposé: si c'est raisonnable, si les inconvénients sont susceptibles de causer des erreurs graves, et s'il y a d'autres inconvénients que je n'ai pas encore considérés qui pourraient empêcher le système de fonctionner entièrement. Je serais également heureux d'entendre toutes les autres solutions possibles auxquelles je n'avais pas pensé.

Répondre

1

Je ferais quelque chose comme # 4, mais ne générez pas de sortie tant que vous n'êtes pas sûr que l'entrée est valide.

  • mbrtowc doit décoder le caractère entier. Si elle est en dehors du BMP, affichez le substitut haut et stockez le substitut faible dans le mbstate_t.
  • wcrtomb doit stocker des substituts élevés dans le mbstate_t, puis afficher les 4 octets UTF-8 si le caractère est valide.
+0

Cela semble bien, mais je ne suis pas sûr que ce soit possible. Si 'mbrtowc' stocke le substitut bas dans' mbstate_t', alors il devrait sortir un 'wchar_t' lors de l'appel suivant sans consommer d'entrée. Mais la valeur de retour de 0 est réservée à la conversion d'un octet/fin de chaîne nul. Je suppose qu'il pourrait consommer un octet supplémentaire du caractère suivant, mais s'il s'agissait d'un caractère à un octet, l'étrange tampon continuerait. Qu'est-ce que tu penses? –

+0

C'est dur, car la norme C suppose que 'wchar_t' peut représenter n'importe quel caractère, et Microsoft l'a violé. Je ne pense pas qu'il soit possible d'écrire un 'mbrtowc 'conforme avec UTF-16. – dan04

+0

La valeur de retour 0 peut ne pas poser de problème si le code appelant vérifie la valeur de 'wch' au lieu de supposer U + 0000 en fonction de la valeur de retour. Je n'utilise pas 'mbrtowc' moi-même (à la place, une fonction de conversion développée en interne qui fonctionne sur des chaînes entières), donc je ne suis pas sûr de la quantité de problèmes que cela poserait en pratique. – dan04

0

Si vous utilisez Windows, vous convertissez une chaîne complète entre UTF-16 et UTF-8 à la fois en utilisant MultiByteToWideChar et WideCharToMultiByte. Alors que le mode par défaut dans GCC est un wchar_t 32 bits, il existe des commutateurs de compilation qui changent cela, et plus généralement les spécifications C++ ne spécifient pas la taille de wchar_t - en fait, wchar_t peut avoir la même taille que char.

Si vous voulez éviter d'utiliser les API Windows (dans votre code Windows wrapper !?), utilisez mbstowcs pour convertir une chaîne entière à la fois.

+0

Je pense que vous avez mal compris la question. Une chose que le wrapper doit fournir est les fonctions C 'mbrtowc' et' wcrtomb' (et le reste des fonctions de conversion multibyte/wide qui, en principe, appellent simplement ces fonctions de base), car une application peut les utiliser pour traiter des chaînes en tant que personnages. Fournir ceux-ci est la partie qui est difficile à cause de 16bit 'wchar_t'. Bien sûr, il est facile de fournir n'importe quelle fonction où vous pouvez traiter des chaînes entières à la fois. –