Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Locale pour lire/écrire en UTF-8

15 réponses
Avatar
Sylvain Togni
Bonjour,

J'ai essayé ça :

std::wofstream file("C:/test.txt");
file.imbue(std::locale("UTF-8"));
file << (wchar_t)0x732b;

mais le constructeur de std::locale lance une exception.

Est-ce mon système (VC++6) qui n'a pas de locale UTF-8
ou est-ce que j'utilise pas std::locale de la bonne façon ?

--
Sylvain Togni

10 réponses

1 2
Avatar
kanze
Sylvain Togni wrote:

J'ai essayé ça :

std::wofstream file("C:/test.txt");
file.imbue(std::locale("UTF-8"));
file << (wchar_t)0x732b;

mais le constructeur de std::locale lance une exception.

Est-ce mon système (VC++6) qui n'a pas de locale UTF-8 ou
est-ce que j'utilise pas std::locale de la bonne façon ?


Les conventions du nommage des locales dépendent du système,
mais a priori, le nom d'un locale doit spécifier à la fois la
langue, l'endroit géographique, et l'encodage. Chez moi, par
exemple (mais c'est Solaris, non Windows), j'ai « en_US.UTF-8 ».
A priori, il y aurait aussi un « fr_FR.UTF-8 », mais ce n'est
pas installé.

Malheureusement, non seulement il n'y a pas de normalisation en
ce qui concerne les noms, il n'y a pas non plus de moyen
portable pour déterminer quels locales sont disponibles.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Sylvain Togni

Les conventions du nommage des locales dépendent du système,
mais a priori, le nom d'un locale doit spécifier à la fois la
langue, l'endroit géographique, et l'encodage. Chez moi, par
exemple (mais c'est Solaris, non Windows), j'ai « en_US.UTF-8 ».
A priori, il y aurait aussi un « fr_FR.UTF-8 », mais ce n'est
pas installé.

Malheureusement, non seulement il n'y a pas de normalisation en
ce qui concerne les noms, il n'y a pas non plus de moyen
portable pour déterminer quels locales sont disponibles.


Ok, donc pour avoir quelque chose de portable, il vaut mieux
gérer la conversion soit même, j'imagine.

J'ai essayé la classe utf8_codecvt_facet de boost :

std::wofstream file("C:/test.txt");
file.imbue(std::locale(std::locale(), new utf8_codecvt_facet));
file << L"à";

mais ça produit un fichier de 1 octet, en ISO 8859-1 ou quelque
chose du même genre, mais pas en UTF-8.

Qu'est-ce qui va pas ?
Quelqu'un a déjà utilisé cette classe ?

--
Sylvain Togni

Avatar
Samuel Krempp
le Wednesday 20 April 2005 19:02, écrivit :

file << L"à";


je suis peut-être à coté de la plaque, mais je me méfie de ce que fait le
compilateur des caractères non-ascii, je ferais plutôt des tests en tapant
un caractere d'une façon standard :
uC3A0
(ça devrait être 'à', à moins que je me sois gouré dans mes codes..)

bon, je dis pas que ça va forcément changer qque chose, mais au moins ça
évite d'ajouter d'éventuels problèmes d'interpretation du caractère par le
compilateur.

D'ailleurs, si on veut mettre des caractères non ascii dans un source, le
seul moyen portable est de tous les traduire en "universal character name",
non ?
ça doit pouvoir facilement s'automatiser..

--
Sam

Avatar
kanze
Samuel Krempp wrote:
le Wednesday 20 April 2005 19:02, écrivit :

file << L"à";


je suis peut-être à coté de la plaque, mais je me méfie de ce
que fait le compilateur des caractères non-ascii, je ferais
plutôt des tests en tapant un caractere d'une façon standard :
uC3A0
(ça devrait être 'à', à moins que je me sois gouré dans mes
codes..)


Tu as dû te gourer : uC3A0 est un caractère Hangul. Le code
correct est u00E0. (En général, les codes ISO 8859-1 sont
identique en Unicode, ce qui veut dire que les deux premiers
chiffres d'un u seront toujours 00.)

bon, je dis pas que ça va forcément changer qque chose, mais
au moins ça évite d'ajouter d'éventuels problèmes
d'interpretation du caractère par le compilateur.

D'ailleurs, si on veut mettre des caractères non ascii dans un
source, le seul moyen portable est de tous les traduire en
"universal character name", non ?


Pas vraiment. C'est un peu comme export -- beaucoup de
compilateurs n'implémente pas les « universel character name ».
Chez moi, par exemple, L'à' compile bien avec Sun CC (mais donne
le code 0x30000060 -- pas Unicode du tout), mais g++ le rejette
avec le message d'erreur « converting to execution character
set: Invalid argument » (ce qui est sont droit, même si on
pourrait en discuter au niveau de la qualité de
l'implémentation). En revanche, avec L'u00E0', c'est Sun CC qui
génère deux avertissement : « Warning (Anachronism): Undefined
character escape sequence "u" » et « Warning: Too many
characters in character constant L'u00E0' ». Et le resultat est
un wchar_t à valeur de 0x30.

Dans la pratique, la seule solution réelement fiable, c'est de
lire les messages d'un fichier externe:-(.

Pour les char, 'à' donne les bons résultats avec les deux
compilateurs ; CC n'accepte pas 'u00E0', et curieusement (parce
qu'il supporte manifestement les "universal character name"),
g++ donne un avertisement "warning: multi-character character
constant" et génère 0xA0 !

Je me démande si une partie du problème n'est pas que, 'u'
étant un comportement indéfini selon la norme (C90 aussi), les
implémentations lui ont donné un comportement défini, qu'ils ont
peur de casser.

Ce qui est intéressant, c'est que 'à' marche à peu près partout.
Pour des raisons sans doute historique, quand il s'agit des
"..." ou des '...', les compilateurs que je connais passent le
contenu du fichier de façon transparamment, sans trop poser de
questions. Si lors des sorties sur l'écran, le font utilise le
même encodage que lorsque j'ai édité le fichier source, tout se
passe bien. Sinon...

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
Samuel Krempp writes:

D'ailleurs, si on veut mettre des caractères non ascii
dans un source, le seul moyen portable est de tous les
traduire en "universal character name", non ?


C'est portables aux implémentations conformes, mais est-ce
que les compilateurs courant sont conformes sur ce point?
Ma question est sincère, je ne me suis jamais amusé à
tester.

A+


--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Sylvain Togni
Samuel Krempp writes:

D'ailleurs, si on veut mettre des caractères non ascii
dans un source, le seul moyen portable est de tous les
traduire en "universal character name", non ?


C'est portables aux implémentations conformes, mais est-ce
que les compilateurs courant sont conformes sur ce point?
Ma question est sincère, je ne me suis jamais amusé à
tester.


VC6 se comporte comme Sun CC sur ce point : « warning C4129:
'u' : unrecognized character escape sequence », « warning C4066:
characters beyond first in wide-character constant ignored ».

(Et je crois qu'on peut encore qualifier VC6 de compilateur
courant.)

--
Sylvain Togni


Avatar
Samuel Krempp
le Thursday 21 April 2005 09:16, écrivit :

Tu as dû te gourer : uC3A0 est un caractère Hangul. Le code
correct est u00E0. (En général, les codes ISO 8859-1 sont
identique en Unicode, ce qui veut dire que les deux premiers
chiffres d'un u seront toujours 00.)


oups, comme j'avais pas de référence unicode sous la main j'ai pensé à
utiliser iconv(1) pour trouver le code-point correspondant à 'à', et je
suis tombé dans le piège "codate UTF-8" vs "valeur unicode", que je connais
pourtant..
j'ai tapé :
echo à | iconv -f latin1 -t utf8 | hexdump
au lieu de
echo à | iconv -f latin1 -t unicodebig | hexdump

C3,A0 est l'encodage UTF-8 de u00E0
c'est décidemment très facile de s'emmêler !

le code 0x30000060 -- pas Unicode du tout), mais g++ le rejette
avec le message d'erreur « converting to execution character
set: Invalid argument » (ce qui est sont droit, même si on
pourrait en discuter au niveau de la qualité de
l'implémentation).


si le source a été tapé avec emacs, en latin-1, et que g++ fonctionne en
UTF-8, il voit arriver un caractère non-valide (0xE0) ..

en testant, L'à' (écrit en UTF-8) donne la valeur 0xE0, quelle que soit la
locale au moment de la compilation (comme L'u00E0')

Tandis qu'écrit en latin1, L'à' est invalide :
"""
LANG=fr_FR.UTF-8 g++ testLocale.cpp
testLocale.cpp:31:18: converting to execution character set : Argument
invalide
"""
et mettre LANG=fr_FR ne change rien.


CONCLUSION :
mon g++ suppose un encodage UTF-8 (à l'intérieur de wide character literals,
c'est ptet différent dans d'autres contextes..) indépendamment de la locale
en cours. (ce qui est un choix valable, pour peu que l'utilisateur en aie
conscience !)

[Le man de mon g++ (3.4) semble dire que les variables LANG, LC_TYPE sont
utilisées, mais en tout cas, pas pour ça visiblement.. ]

Dans la pratique, la seule solution réelement fiable, c'est de
lire les messages d'un fichier externe:-(.


ouais, c'est un peu dommage..
enfin, visiblement avec g++ on doit pouvoir faire ce qu'on veut en UTF-8,
avec un peu de chance VC++ et autres peuvent aussi comprendre des sources
encodées en UTF-8 (ça serait un comportement à la fois portable et
pratique..)

Pour les char, 'à' donne les bons résultats avec les deux
compilateurs ; CC n'accepte pas 'u00E0', et curieusement (parce
qu'il supporte manifestement les "universal character name"),
g++ donne un avertisement "warning: multi-character character
constant" et génère 0xA0 !


à l'intérieur d'un character-literal NON-wide, c'est un warning naturel,
non ?
g++3.4 me dit pareil, et le code (tapé en latin-1) :
cout << hex << (int) ((unsigned char) 'à') << endl;
cout << 'u00E0' << endl;

affiche [*quelle que soit* la locale, à la compil aussi bien qu'à
l'éxécution ] :
e0
[logique, g++ passe la valeur 0xE0 sans la toucher]
puis :
c3a0 [ qui est le codage UTF-8 de 'à']
il pourrait faire une erreur plutot qu'un warning, non ?
un char literal avec une telle valeur, c'est un peu bizarre :)
hmm, ça permet de mettre des caractères unicodes dans des string literals,
qui seront implicitement convertis en UTF-8 (non portable je suppose, mais
eventuellement pratique)
"u00e0" -> séquence des octets correspondant à l'encodage UTF-8 de 'à'.

Ce qui est intéressant, c'est que 'à' marche à peu près partout.
Pour des raisons sans doute historique, quand il s'agit des
"..." ou des '...', les compilateurs que je connais passent le
contenu du fichier de façon transparamment, sans trop poser de
questions. Si lors des sorties sur l'écran, le font utilise le
même encodage que lorsque j'ai édité le fichier source, tout se
passe bien. Sinon...


c'est tout à fait ma conclusion aussi.
les literals normaux (pas L'...' ou L"...") sont manipulés tels quels, le
compilateur n'a en fait aucun besoin de s'occupper d'encodage des
caractères non-ASCII.

Dans des wide-character literals par contre, l'interpretation des octets du
source en caractères wides suppose de connaître l'encodage du source, et
visiblement g++ impose de les taper en UTF-8 (ou de taper des 'universal
character name', façon u00e0)

bon, je vais garder tout ça qque part, c'est pas la première fois que je me
pose ces questions et je n'arrive jamais à me souvenir de la conclusion ..
--
Sam

Avatar
Samuel Krempp
le Thursday 21 April 2005 09:16, écrivit :

Tu as dû te gourer : uC3A0 est un caractère Hangul. Le code
correct est u00E0. (En général, les codes ISO 8859-1 sont
identique en Unicode, ce qui veut dire que les deux premiers
chiffres d'un u seront toujours 00.)


oups, comme j'avais pas de référence unicode sous la main j'ai pensé à
utiliser iconv(1) pour trouver le code-point correspondant à 'à', et je
suis tombé dans le piège "codage UTF-8" vs "valeur unicode", que je connais
pourtant..
j'ai tapé :
echo à | iconv -f latin1 -t utf8 | hexdump
au lieu de
echo à | iconv -f latin1 -t unicodebig | hexdump

C3,A0 est l'encodage UTF-8 de u00E0
c'est décidemment très facile de s'emmêler !

le code 0x30000060 -- pas Unicode du tout), mais g++ le rejette
avec le message d'erreur « converting to execution character
set: Invalid argument » (ce qui est sont droit, même si on
pourrait en discuter au niveau de la qualité de
l'implémentation).


si le source a été tapé avec emacs, en latin-1, et que g++ fonctionne en
UTF-8, il voit arriver un caractère non-valide (0xE0) ..

en testant, L'à' (écrit en UTF-8) donne la valeur 0xE0, quelle que soit la
locale au moment de la compilation (comme L'u00E0')

Tandis qu'écrit en latin1, L'à' est invalide :
"""
LANG=fr_FR.UTF-8 g++ testLocale.cpp
testLocale.cpp:31:18: converting to execution character set : Argument
invalide
"""
et mettre LANG=fr_FR ne change rien.


CONCLUSION :
mon g++ suppose un encodage UTF-8 (à l'intérieur de wide character literals,
c'est ptet différent dans d'autres contextes..) indépendamment de la locale
en cours. (ce qui est un choix valable, pour peu que l'utilisateur en aie
conscience !)

[Le man de mon g++ (3.4) semble dire que les variables LANG, LC_TYPE sont
utilisées, mais en tout cas, pas pour ça visiblement.. ]

Dans la pratique, la seule solution réelement fiable, c'est de
lire les messages d'un fichier externe:-(.


ouais, c'est un peu dommage..
enfin, visiblement avec g++ on doit pouvoir faire ce qu'on veut en UTF-8,
avec un peu de chance VC++ et autres peuvent aussi comprendre des sources
encodées en UTF-8 (ça serait un comportement à la fois portable et
pratique..)

Pour les char, 'à' donne les bons résultats avec les deux
compilateurs ; CC n'accepte pas 'u00E0', et curieusement (parce
qu'il supporte manifestement les "universal character name"),
g++ donne un avertisement "warning: multi-character character
constant" et génère 0xA0 !


à l'intérieur d'un character-literal NON-wide, c'est un warning naturel,
non ?
g++3.4 me dit pareil, et le code (tapé en latin-1) :
cout << hex << (int) ((unsigned char) 'à') << endl;
cout << 'u00E0' << endl;

affiche [*quelle que soit* la locale, à la compil aussi bien qu'à
l'éxécution ] :
e0
[logique, g++ passe la valeur 0xE0 sans la toucher]
puis :
c3a0 [ qui est le codage UTF-8 de 'à']
il pourrait faire une erreur plutot qu'un warning, non ?
un char literal avec une telle valeur, c'est un peu bizarre :)
hmm, ça permet de mettre des caractères unicodes dans des string literals,
qui seront implicitement convertis en UTF-8 (non portable je suppose, mais
eventuellement pratique)
"u00e0" -> séquence des octets correspondant à l'encodage UTF-8 de 'à'.

Ce qui est intéressant, c'est que 'à' marche à peu près partout.
Pour des raisons sans doute historique, quand il s'agit des
"..." ou des '...', les compilateurs que je connais passent le
contenu du fichier de façon transparamment, sans trop poser de
questions. Si lors des sorties sur l'écran, le font utilise le
même encodage que lorsque j'ai édité le fichier source, tout se
passe bien. Sinon...


c'est tout à fait ma conclusion aussi.
les literals normaux (pas L'...' ou L"...") sont manipulés tels quels, le
compilateur n'a en fait aucun besoin de s'occupper d'encodage des
caractères non-ASCII.

Dans des wide-character literals par contre, l'interpretation des octets du
source en caractères wides suppose de connaître l'encodage du source, et
visiblement g++ impose de les taper en UTF-8 (ou de taper des 'universal
character name', façon u00e0)

bon, je vais garder tout ça qque part, c'est pas la première fois que je me
pose ces questions et je n'arrive jamais à me souvenir de la conclusion ..
--
Sam

Avatar
Samuel Krempp
le Thursday 21 April 2005 09:16, écrivit :

Tu as dû te gourer : uC3A0 est un caractère Hangul. Le code
correct est u00E0. (En général, les codes ISO 8859-1 sont
identique en Unicode, ce qui veut dire que les deux premiers
chiffres d'un u seront toujours 00.)


oups, comme j'avais pas de référence unicode sous la main j'ai pensé à
utiliser iconv(1) pour trouver le code-point correspondant à 'à', et je
suis tombé dans le piège "codage UTF-8" vs "valeur unicode", que je connais
pourtant..
j'ai tapé :
echo à | iconv -f latin1 -t utf8 | hexdump
au lieu de
echo à | iconv -f latin1 -t unicodebig | hexdump

C3,A0 est l'encodage UTF-8 de u00E0
c'est décidemment très facile de s'emmêler !

le code 0x30000060 -- pas Unicode du tout), mais g++ le rejette
avec le message d'erreur « converting to execution character
set: Invalid argument » (ce qui est sont droit, même si on
pourrait en discuter au niveau de la qualité de
l'implémentation).


si le source a été tapé avec emacs, en latin-1, et que g++ fonctionne en
UTF-8, il voit arriver un caractère non-valide (0xE0) ..

en testant, L'à' (écrit en UTF-8) donne la valeur 0xE0, quelle que soit la
locale au moment de la compilation (comme L'u00E0')

Tandis qu'écrit en latin1, L'à' est invalide :
"""
LANG=fr_FR.UTF-8 g++ testLocale.cpp
testLocale.cpp:31:18: converting to execution character set : Argument
invalide
"""
et mettre LANG=fr_FR ne change rien.


CONCLUSION :
mon g++ suppose un encodage UTF-8 (à l'intérieur de wide character literals,
c'est ptet différent dans d'autres contextes..) indépendamment de la locale
en cours. (ce qui est un choix valable, pour peu que l'utilisateur en aie
conscience !)

[Le man de mon g++ (3.4) semble dire que les variables LANG, LC_TYPE sont
utilisées, mais en tout cas, pas pour ça visiblement.. ]

Dans la pratique, la seule solution réelement fiable, c'est de
lire les messages d'un fichier externe:-(.


ouais, c'est un peu dommage..
enfin, visiblement avec g++ on doit pouvoir faire ce qu'on veut en UTF-8,
avec un peu de chance VC++ et autres peuvent aussi comprendre des sources
encodées en UTF-8 (ça serait un comportement à la fois portable et
pratique..)

Pour les char, 'à' donne les bons résultats avec les deux
compilateurs ; CC n'accepte pas 'u00E0', et curieusement (parce
qu'il supporte manifestement les "universal character name"),
g++ donne un avertisement "warning: multi-character character
constant" et génère 0xA0 !


à l'intérieur d'un character-literal NON-wide, c'est un warning naturel,
non ?
g++3.4 me dit pareil, et le code (tapé en latin-1) :
cout << hex << (int) ((unsigned char) 'à') << endl;
cout << 'u00E0' << endl;

affiche [*quelle que soit* la locale, à la compil aussi bien qu'à
l'éxécution ] :
e0
[logique, g++ passe la valeur 0xE0 sans la toucher]
puis :
c3a0 [ qui est le codage UTF-8 de 'à']
il pourrait faire une erreur plutot qu'un warning, non ?
un char literal avec une telle valeur, c'est un peu bizarre :)
hmm, ça permet de mettre des caractères unicodes dans des string literals,
qui seront implicitement convertis en UTF-8 (non portable je suppose, mais
eventuellement pratique)
"u00e0" dans le source est donc traité comme la séquence des octets
correspondant à l'encodage UTF-8 de 'à'.

Ce qui est intéressant, c'est que 'à' marche à peu près partout.
Pour des raisons sans doute historique, quand il s'agit des
"..." ou des '...', les compilateurs que je connais passent le
contenu du fichier de façon transparamment, sans trop poser de
questions. Si lors des sorties sur l'écran, le font utilise le
même encodage que lorsque j'ai édité le fichier source, tout se
passe bien. Sinon...


c'est tout à fait ma conclusion aussi.
les literals normaux (pas L'...' ou L"...") sont manipulés tels quels, le
compilateur n'a en fait aucun besoin de s'occupper d'encodage des
caractères non-ASCII.

Dans des wide-character literals par contre, l'interpretation des octets du
source en caractères wides suppose de connaître l'encodage du source, et
visiblement g++ impose de les taper en UTF-8 (ou de taper des 'universal
character name', façon u00e0)

bon, je vais garder tout ça qque part, c'est pas la première fois que je me
pose ces questions et je n'arrive jamais à me souvenir de la conclusion ..
--
Sam

Avatar
kanze
Jean-Marc Bourguet wrote:
Samuel Krempp writes:

D'ailleurs, si on veut mettre des caractères non ascii
dans un source, le seul moyen portable est de tous les
traduire en "universal character name", non ?


C'est portables aux implémentations conformes,


Export aussi, mais tu ne me vois pas en train de dire aux gens
d'utiliser export dans du code portable.

mais est-ce que les compilateurs courant sont conformes sur ce
point?


Sur ceux que j'ai testé ici, un sur deux. Et je n'ai essayé
qu'avec les dernières versions, alors que pour des raisons
pratiques, la version de g++ qu'on utiliser réelement est encore
2.95.2. (La raison pratique, évidemment, c'est qu'on n'a pas le
budget pour porter tout le code existant, qui évidemment
n'utilise pas typename où il faut.)

Ma question est sincère, je ne me suis jamais amusé à tester.


Il serait en effet intéressant à savoir. Je n'ai accès qu'au
deux compilateurs actuellement (plus Comeau chez moi), bien
qu'avec plusieurs versions de chacun. A priori, c'est une
modification assez facile, sur le plan technique. Sur le plan
politique, c'est moins évident.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2