Bonjour,
J'aimerais savoir comment convertir simplement un wstring en char*.
Merci.:)
Version imprimable
Bonjour,
J'aimerais savoir comment convertir simplement un wstring en char*.
Merci.:)
Merci.
J'ai ajouter les fonctions à mon projet dans un fichier Tool.h
et j'ai l'erreur :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 #include <string> #include <locale> namespace Tool { std::string WStringToString(const std::wstring& ws) { std::string res(ws); std::locale loc("english"); std::use_facet< std::ctype<wchar_t> >(loc).narrow(&ws[0], &ws[ws.length()], '?', &res[0]); return res; } std::wstring StringToWString(const std::string& s) { std::wstring res(s); std::locale loc("english"); std::use_facet< std::ctype<wchar_t> >(loc).widen(&s[0], &s[s.length()], &res[0]); return res; } }
Pourquoi ?Code:
1
2 Erreur 1 tool.h 9 error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(std::basic_string<_Elem,_Traits,_Ax>::_Has_debug_it)' : impossible de convertir le paramètre 1 de 'const std::wstring' en 'std::basic_string<_Elem,_Traits,_Ax>::_Has_debug_it' Erreur 2 tool.h 18 error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(std::basic_string<_Elem,_Traits,_Ax>::_Has_debug_it)' : impossible de convertir le paramètre 1 de 'const std::string' en 'std::basic_string<_Elem,_Traits,_Ax>::_Has_debug_it'
Pourquoi 3 erreurs en 3 lignes, telle est la question.
Reprenons :
std::string res(ws);
Déjà, ça ne risque pas de compiler. Une chaine de char n'est pas une chaine de wchar_t. Probablement fallait-il lire
std::string res(ws.size());
puisque de toutes façons le contenu sera effacé, il faut juste créer les caractères à écraser.
std::locale loc("english");
Qui dit qu'une telle locale existe? Sur un système Un*x, je ne pense pas que risque d'être le cas. Peut-être une locale "en" existera t-elle, mais en quoi en avons nous besoin?
Surtout que doit faire une telle "locale"? La "locale" devrait regrouper les caractéristiques locales. En quoi les facets ctype et codecvt sont-elles des propriétés locales (= préférences régionales)? En rien évidemment. Ce sont des caractéristiques de jeux de caractères. Bien sûr, ISO-8859-5 ne permet pas d'écrire le français, et "donc" ne se trouvera pas dans une locale française ("fr", "fr_FR", "french"...). Mais, en réalité, ISO-8859-1 non plus. La relation entre locale et jeu de caractères est complexe et dépendant de l'histoire. On trouve d'ailleurs des locales appelée "fr.utf8". Ridicule.
Cette absurdité prend racine quelque part dans Unix. C++ a suivit dans la voie du non-sens et de l'inutilisable, évidemment.
En résumé : le nom des locales est dépendant du système et, en terme de jeu de caractère, sa signification peu claire. De toutes façons, le jeu de caractère est une propriété d'un ressource (fichier, flux, chaine...), pas d'une région/culture (mais un utilisateur peut typiquement avoir un codage préféré pour les fichiers de sortie).
Ici, la ressource en entrée est une chaine large, donc normalement composée de code-point Unicode : UCS-2 ou UCS-4. (Mais on risque aussi de trouver de l'UTF-16. Beurk. Encore une question à préciser.) Le codage est donc à peu-près clair (nous verrons que non, pas tout à fait).
En sortie par contre, il faudrait définir le jeu de caractère à utiliser : est-ce défini par un protocole (= une norme), par le concepteur du programme, ou par la préférence de l'utilisateur (seul cas où une locale me semble adéquate). Pour les sorties, une variable d'environnement spécifique (qui n'influe pas les autres paramètres de localisation), ou une option en ligne de commande est souvent le seul réglage suffisamment flexible pour s'adapter aux différences de jeux de caractères.
Enfin, si la locale "english" signifie comme je le pense "iso-latin-1", la conversion est triviale :
c = wc < 256 : (char)wc : '?';
En effet les valeurs Unicode correspondent intentionnellement à iso-latin-1, pour la bonne notion de correspondance : encore faut-il être en forme NFC (ou NFKC...), c'est à dire qu'une lettre doit être décrite par un seul code-point, par exemple : "à" doit être codée LATIN SMALL LETTER A WITH GRAVE et non LATIN SMALL LETTER A suivit de COMBINING GRAVE ACCENT.
Je ne sais pas ce que font les implémentations de codecvt des formes D (LATIN SMALL LETTER A suivit de COMBINING ACUTE ACCENT) mais la norme dit bien qu'elles n'ont pas à les gérer, pas plus qu'un wchar_t en UTF-16 (puisque la représentation interne ne doit pas avoir plusieurs "éléments" (wchar_t) pour une séquence d'éléments externes, représentant une lettre). Manifestement, ce genre de choses n'est pas prévu, pour preuve l'interface utilisée ici : narrow.
Je mentionne ça en passant, il faut s'assurer quand on parle de wstring si :
- il s'agit bien d'UCS-* (comme on l'attend pour une représentation interne) ou d'UTF-16 (comme Java, assez hypocritement)
- on exige la forme NFC (forme "courte"), NFD, ou n'importe; mais très franchement les diacritiques combinant sont une débilité sans nom inventée par Unicode, je n'imagine pas que votre code soit compatible avec ça (en faisant quelque traitement - autre que lire un chaine recopier la chaine) (les code-point combinant Unicode DOIVENT MOURIR, faites passer le mot) (reste qu'ils ne sont pas encore mort - et à peine nés)
En général on fait les deux hypothèses ci-dessus sans le dire, mais c'est quand même un beau bordel (et passablement hypocrite aussi).
Dans la ligne :
std::use_facet< std::ctype<wchar_t> >(loc).narrow(&ws[0], &ws[ws.length()], '?', &res[0]);
2 erreurs
&ws[0], &ws[ws.length()]
Il n'est pas garanti que la représentation d'une string soit contigüe (contrairement à vector).
&res[0]
Même problème, il n'est pas garanti que &res[0], &res[0] + res.size() soit une range valide.
En plus, &ws[ws.length()] désigne un NUL à la fin de la chaine uniquement parce que ws est const. C'est une idée hyper-vicieuse de la norme et c'est hyper-dangereux : si, dans une évolution future du programme, ws devenait non-const (par exemple, si ça devenait une variable locale de la fonction), le code casserai, silencieusement.
Non seulement c'est immensément crétin de la part de la norme de différencier const/non-const comme ça, en plus, la description ne veut rien dire :
the const version returns charT()
D'après la norme, cette fonction renvoie une référence sur un temporaire! Lapsus normatif? (genre, je ne spécifie pas du tout ce qui est immonde (vector<bool>), ou alors n'importe comment (ici)).
Fonctionnalité à éviter absolument en tous cas. La surcharge de const ne sert part à ça! (quelque fois, la lib standard ne comprend comment les fonctionnalités du C++ sont censées être utilisées)
Il me semblait bien que ce code était mauvais quand je l'ai vu dans la FAQ.
Si je me souviens bien, la norme définit seulement deux noms de locale: "C" et "" (chaîne vide).
Ensuite, les auteurs de la SL se sont bien débrouillés pour qu'il n'y ait absolument aucun moyen d'obtenir un accès direct en écriture sur le buffer d'une std::string (contrairement à ceux de MFC qui proposent CString::GetBuffer()).
Donc, si je comprends bien, il est:
- D'une part, impossible de convertir en un seule passe une string en wstring avec un seul appel de narrow() (on doit faire des append() successifs, ou passer par un buffer),
- D'autre part, impossible de spécifier de manière portable une locale (mais "" devrait suffire dans la plupart des cas).
narrow c'est un appel virtuel (à l'intérieur), je doute que tu veuille faire un appel par caractère!
Ceci vaut aussi pour ctype<charT>, codecvt. On accumule quelques caractères dans un buffer avant d'appliquer l'opération (virtuelle). C'est aussi le principe de streambuf. Le buffer va typiquement être de taille fixe, et l'opération répétée en boucle (donc ça n'induit aucune limite sur la quantité de données traitées).
Note : Il ne faut pas dramatiser le coup des fonctions virtuelles. Juste que par rapport à convertir ou classifier 1 caractère, c'est monstrueusement couteux. (Non, je ne vais pas m'amuser à faire des tests pour le prouver.)
Encore une fois, si c'est pour convertir de UCS-* (NFC) en ISO-8859-1, il y a plus simple (et efficace et fiable)!
(Encore une fois, si ce n'est pas du NFC (ou NFKC...), la bibliothèque standard n'est pas utilisable. Si tu importes de l'Unicode de l'extérieur, fais attention à ça.)
En pratique, et pour le futur, c'est garanti :
http://www.open-std.org/jtc1/sc22/wg...fects.html#530
Edit : La partie indentée de ce message est n'importe quoi. Merci de n'en pas tenir compte.
Fin d'édit
Pour le vector<bool>, qu'est-ce qui n'est pas spécifié ? Pour moi, ils ont voulu trop spécifier justement, et c'est tombé à côté...
Précisément, non :
Proposed resolution:
Add the following text to the end of 21.3 [basic.string], paragraph 2.
The characters in a string are stored contiguously, meaning that if s is a basic_string<charT, Allocator>, then it obeys the identity &*(s.begin() + n) == &*s.begin() + n for all 0 <= n < s.size().
Le NUL terminal n'est pas in la chaine et ws.length() n'est pas strictement inférieur à ws.size(), donc &ws[0], &ws[ws.length()] n'est pas une range valide, et la ligne
est incorrecte.Code:std::use_facet< std::ctype<wchar_t> >(loc).narrow(&ws[0], &ws[ws.length()], '?', &res[0]);
Je ne pense pas. Et je ne comprends pas ce que tu dis : est-ce que tu penses que
est correct? :?Code:
1
2
3 const int &f () { return int(); }
La norme (je prends le dernier draft n2521) n'est pas de cet avis, dans le paragraphe que tu cites, 12.2/5 (pas 12.2.5) :
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except as specified below. (...) A temporary bound to the returned value in a function return statement (6.6.3) persists until the function exits.En dehors de la tournure très surprenante ("A temporary bound to a reference", répété plusieurs fois, alors que le paragraphe commence par "a reference is bound to a temporary", ce qui est la terminologie normale), il me semble très clair. Si ce n'est pas assez clair, quelque chose comme cela devrai être écrit :
When a reference return value is bound to a temporary, the temporary persists until the function exits.
(J'ai du relire plusieurs fois la phrase originale; quelle idée d'écrire comme ça? Savent-ils écrire en anglais?)
Justement, dans la norme, rien, à part des déclarations. On ne sait pas ce à quoi sert reference::flip(). Comme il n'y a aucune relation explicitée entre vector<bool> et la forme de base de vector, on ne sait ce que fait aucune fonction membre.
le problème n'est pas la présence du NULL terminal puisqu'une étendue de &ws[0] à &ws[ws.length()] va des éléments de 0 à ws.length()-1 (= ws.size()-1 ), le "end()" d'un itérateur à accès aléatoire pointant sur l'adresse de l'élément se trouvant juste APRES le dernier élément du tableau (élément auquel il ne faut bien sur pas tenter d'accéder). La seule crainte pourrait être que l'opérateur [] dans ws[ws.length()] vérifie que l'indice soit strictement plus petit que ws.length() (et lève une exception dans le cas contraire), mais cela m'étonnerais, surtout si cela est implémenté comme pourIl faudrait juste voir ce que défini la norme sur l'operateur [] pour les basic_stringCitation:
Clause 23.3.4 [multiset] defines operator[] as data()[pos]
(Tu voulais dire NUL, je suppose.) Je n'ai jamais parlé de la présence de NUL.
Cette étendue (range) n'est pas valide, elle n'existe pas, comme je l'ai indiqué.
Elle ne va nul part puisqu'elle n'existe pas.
Rappelons que le code est :
Où vois-tu "end()"?Code:std::use_facet< std::ctype<wchar_t> >(loc).narrow(&ws[0], &ws[ws.length()], '?', &res[0]);
Ou un même un itérateur "juste APRES le dernier élément" ("one-past-the-end")?
Non, elle ne va pas faire cela parce que l'indice n'a pas à être strictement inférieur à ws.length(). L'expression &ws[ws.length()] est absolument légale, comme je l'ai indiqué (mais elle est aussi très fortement déconseillée).
Clause 23.3.4 [multiset] defines operator[] as data()[pos]
23.3.4 fait 4 pages! (je regarde n2315, là) Tu peux préciser?
Et depuis quand multiset définit data()?
Par contre, à la question : est-ce que string peut faire des vérifications de bornes dans operator[], la réponse est oui, bien sûr.
Comment ça? Si une fonction n'est pas spécifiée, on ne peut pas l'utiliser!
Dès lors qu'une fonction exige que ses arguments soient dans un certain domaine, l'implémentation de la fonction peut bien évidemment le vérifier.
Tu es sûr que Visual C++ le fait toujours et pas simplement dans la configuration par défaut? Tu es sûr que la libc++ ne peut pas le faire avec les -D qui vont bien?
Solution : préciser la question.
Qu'a t-on en entrée, que veut-on en sortie?
Voir aussi mon premier message (#5) à propos d'UCS-*/UTF-16 et NFC/NFD.
Pour vector (a vérifier pour les autres), visual utilise un test, mais on peut le désactiver. Gcc n'en fait pas. Du moins dans les dernière source que j'ai regardé : visual 2005 et gcc 3.4
Pour être plus précis,dans la norme, l'operateur [] ne spécifie pas si il doit y avoir ou non un test..
Pour être encore plus précis, si les arguments d'une fonction ne sont pas dans son domaine, sauf mention contraire, le comportement est indéfini. Autrement dit, c'est une précondition : tu dois passer les bon arguments, dans le cas contraire, tu ne peux rien exiger parce que tu as rompu ton contrat.
Je parlais du fait que le stockage soit continu, comme sur un vecteur. Pour ce qui est de l'accès à &ws[ws.length()], on est d'accord sur le fait que ce soit une erreur. Ce serait aussi une erreur pour un vecteur, la bonne écriture étant &ws[0] + ws.size()
Non, j'ai dit n'importe quoi. On va mettre ça sur le dos de l'heure tardive.
A tête un peu plus reposée, je dirais juste que c'est une notation pour indiquer que le fonction retourne une référence constante à un caractère nul. A l'implémentation de se débrouiller pour que ce soit une référence valide.
Ils ont un peu amélioré ça dans le brouillon du prochain standard, mais pour moi et pour beaucoup, l'erreur est d'avoir spécialisé vector<bool>, tout simplement :
Citation:
Envoyé par Le brouillon du standard
Je préfère ws.data() + ws.size(), ça marche même si wc est vide.
Dans le brouillon de norme N2315, data est aussi défini pour vector (23.2.5.3 [vector.data]).
Mettre quoi sur le dos de l'heure tardive? ;)
Notation pourrie.
Effectivement : pour tout T, vector<T>::reference est une reference, donc vector<T>::operator[] renvoie une lvalue, sauf pour T = bool. :evilred:
De plus, dans la table des matières du brouillon de norme (N2315) :
23.2.5 Class template vector
23.2.6 Class vector<bool>
ce n'est pas la même section!
Si ce tu n'as pas du NFC en entrée, ça va être plutôt coton, et la bibliothèque standard ne va pas marcher.
La solution générale, c'est une bibliothèque implémentant Unicode, mais je ne les connais pas précisément. Une telle bibliothèque va reconnaitre plein de jeux de caractères et faire les conversions dans tous les sens.
Pour les conversions, il y a iconv. Mais le non-NFC, voir ce message datant de : 18 Jan 2004! Je n'ai rien trouvé de plus récent.:cry:
Oui je voulais parler du '\0' :oops: Je voulais dire que narrow utilise ses deux premiers paramètres "comme" deux iterateurs ( sauf que se sont forcément des pointeurs qui sont attendu la) et que de ce fait le second paramètre attendu doit être un pointeur sur "one-past-the-end". Mais c'est vrai que l'écriture &ws[ws.length()] :?.Je me demandais juste ce qui était dit sur operator[] pour string. J'ai juste cité une partie de l'entrée en demande comment c'était défini pour les string.
Mais ce qui me dérange le plus c'est le &res[0] danson va tenter d'écrire dans la string à partir de son premier élément ; il n'y a pas que la continuité qui peut poser problème. Cela touche à l'implémentation même des string (si elle partage un même buffer, ... :aie:). A mon avis il vaut mieux passer par un petit vector intermédiaire ;).Code:std::use_facet< std::ctype<wchar_t> >(loc).narrow(&ws[0], &ws[ws.length()], '?', &res[0]);
Un truc du genre :attention c'est juste une idée d'implémentation, non garantie :lol:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13 std::string narrow(const std::wstring& ws , std::locale loc = std::locale("") ) { std::vector<char> temp( ws.length() ); std::use_facet< std::ctype<wchar_t> >(loc).narrow( ws.data() , ws.data()+ws.length(), '?', &temp[0]); return std::string(temp.begin(),temp.end()); } std::wstring widen(const std::string& s, std::locale loc = std::locale("")) { std::vector<wchar_t> temp( s.length() ); std::use_facet< std::ctype<wchar_t> >(loc).widen( s.data() , s.data()+s.length(), &temp[0]); return std::wstring(temp.begin(),temp.end()); }
Perso, je verais bien un truc comme ca :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 // XXX : type d'encodage struct widenToNarrow_XXX { char_t operator()(const wchar_t & c) { //code de conversion pour un encodage donné } } template <type converter> std::string narrow(const std::wstring& ws ) { std::vector<char> temp( ws.length() ); std::transform(ws.begin(),ws.end(),temp.begin(),converter()); return temp; } // XXX : type d'encodage struct narrowToWiden_XXX { wchar_t operator()(const char_t & c) { //code de conversion pour un encodage donné } } template <type converter> std::wstring widen(const std::string& s) { std::vector<wchar_t> temp( s.length() ); std::transform(s.begin(),s.end(),temp.begin(),converter()); return temp; }
Plutôt l'inverse, non (un wchar_t encodé sur plusieurs char) ?
Remarque, c'est aussi possible dans l'autre sens, si les wchar_t sont en UTF-16 (comme sous Windows)...
Et comme cela?
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 // XXX : type d'encodage struct widenToNarrow_XXX { widenToNarrow_XXX() : m_s("") {}; void operator()(const wchar_t & c) { //code de conversion pour un encodage donné //remplie m_s } std::string m_s; } template <type converter> std::string narrow(const std::wstring& ws ) { return std::for_each(s.begin(),s.end(),converter()).m_s; } // XXX : type d'encodage struct narrowToWiden_XXX { narrowToWiden_XXX(): m_s(""){}; void operator()(const char_t & c) { //code de conversion pour un encodage donné //remplie m_s } std::wstring m_s; } template <type converter> std::wstring widen(const std::string& s) { return std::for_each(s.begin(),s.end(),converter()).m_s; }
Si tu te restreint à une correspondance 1 à 1, tu élimines les deux cas! ;)
Sérieusement, réponse : non.
Mais, comme je l'ai indiqué, dans le sens wchar_t -> char, quand plusieurs wchar_t représentent un seul char, voir l'exemple que j'ai donné dans mon premier message dans cette discussion (#5) :
Est-ce qu'on veut qu'une wstring en NFD, qui donc représente 'à' comme {'LATIN SMALL LETTER A', 'COMBINING GRAVE ACCENT'} soit traduite en iso-latin-1 par 'à', où que la conversion produise "a?", '?' représentant l'échec de conversion?Citation:
Envoyé par corrector
Mongaulois:
Sympa, mais je vois plus un ostringstream qu'une string, sur le coup...
Je ne pensais même pas à UTF-16, qui effectivement peut poser problème en général, mais pas forcément dans ce cas. Connais-tu des exemples de "surrogates pairs" qui se traduisent par un seul caractère dans un encodage mono-octet?
Je pensais au codage "idéal" des wchar_t en UCS-4, comme par exemple sous Linux.
OK, j'ai compris le coup des accents.
Quant aux surrogates pairs, disont que je ne connais aucun encodage mono-octet (ou variable 1-2) qui le fasse pour ne serait-ce qu'un caractère, mais je ne suis pas sur non plus qu'il n'en existe aucun, parmi tous les encodages qui existent (ou même en restreignant aux pages de codes supportées par Windows).
En y réfléchissant.
Pourquoi une wstring pourrai encoder un caractère sur plusieurs wchar_t???
Pour fichier ok, mais pour une string. Je ne voie pas
Ben, justement pour les deux raisons qu'on vient de citer: Les accents, et le fait que les wchar_t sous Windows 2000 et supérieur* soient en UTF-16 ?
*Windows NT 4 ne supportait que l'UCS-2