OVH Cloud OVH Cloud

Ignorer temporairement une locale

6 réponses
Avatar
Vincent Richard
Bonjour,

Mon titre n'est peut-être pas très bien choisi. J'ai besoin d'écrire des
nombres dans un flux sans qu'ils soient formattés (comme avec la locale
"C" par défaut).

Soit le code suivant :

----------------------------------------------------------------------
#include <iostream>
#include <locale>

int main()
{
std::locale loc("fr_FR.UTF-8");
std::cout.imbue(loc);

plop(std::cout);
}

void plop(std::ostream& os)
{
os << 123456 << std::endl;
}
----------------------------------------------------------------------

La sortie affichée est, sur ma machine : "123 456" (avec un espace
comme séparateur de milliers).

J'ai besoin que la sortie soit "123456" (brut, sans aucun formattage
donc), tout en laissant le changement de locale dans main (la fonction
plop est en réalité dans une bibliothèque à part).

Est-ce possible ? Existe-t-il un manipulateur qui permet d'inhiber,
temporairement, la locale courante ?

Il me semble qu'il doit être possible de la changer, puis de rétablir
l'originale ensuite, mais ce n'est pas trop ce que je cherche...

En passant, à part les nombres (entiers ou flottants), quels types
sont susceptibles d'êtres "localisés" en sortie ?

Merci d'avance !

Vincent

6 réponses

Avatar
Jean-Marc Bourguet
Vincent Richard writes:

Est-ce possible ? Existe-t-il un manipulateur qui permet d'inhiber,
temporairement, la locale courante ?


Je n'en vois pas.

Il me semble qu'il doit être possible de la changer, puis de rétablir
l'originale ensuite, mais ce n'est pas trop ce que je cherche...


Utiliser un ostream temporaire qui a le meme streambuf me semble etre alors
une solution a investiguer. Je ne peux garantir qu'elle fonctionne.

En passant, à part les nombres (entiers ou flottants), quels types sont
susceptibles d'êtres "localisés" en sortie ?


Les bool peuvent l'etre egalement. D'autres types si ceux qui les ont
fournis ont pris la peine de definir ce qu'il faut.

A+

--
Jean-Marc

Avatar
Michel Decima
Vincent Richard writes:

Il me semble qu'il doit être possible de la changer, puis de rétablir
l'originale ensuite, mais ce n'est pas trop ce que je cherche...


Utiliser un ostream temporaire qui a le meme streambuf me semble etre alors
une solution a investiguer. Je ne peux garantir qu'elle fonctionne.


il y a aussi ca:

void plop(std::ostream& os)
{
std::ostringstream fmt;
fmt << 123456;
os << fmt.str();
}

mais je crois que je prefererais changer la locale pour la retablir apres.


Avatar
Jean-Marc Bourguet
Michel Decima writes:

Vincent Richard writes:

Il me semble qu'il doit être possible de la changer, puis de rétablir
l'originale ensuite, mais ce n'est pas trop ce que je cherche...
Utiliser un ostream temporaire qui a le meme streambuf me semble etre

alors
une solution a investiguer. Je ne peux garantir qu'elle fonctionne.


il y a aussi ca:

void plop(std::ostream& os)
{
std::ostringstream fmt;
fmt << 123456;
os << fmt.str();
}

mais je crois que je prefererais changer la locale pour la retablir apres.


A noter qu'il faut etre prudent en changeant la locale et ne pas imbuer
std::locale::classic(): il faut la recuperer et ne changer que les facets
qu'il faut; en particulier, changer codecvt me semble etre une mauvaise
idee.

A+

--
Jean-Marc



Avatar
James Kanze
Vincent Richard wrote:

Mon titre n'est peut-être pas très bien choisi. J'ai besoin
d'écrire des nombres dans un flux sans qu'ils soient formattés
(comme avec la locale "C" par défaut).

Soit le code suivant :

----------------------------------------------------------------------
#include <iostream>
#include <locale>

int main()
{
std::locale loc("fr_FR.UTF-8");
std::cout.imbue(loc);

plop(std::cout);
}

void plop(std::ostream& os)
{
os << 123456 << std::endl;
}
----------------------------------------------------------------------

La sortie affichée est, sur ma machine : "123 456" (avec un espace
comme séparateur de milliers).

J'ai besoin que la sortie soit "123456" (brut, sans aucun formattage
donc), tout en laissant le changement de locale dans main (la fonction
plop est en réalité dans une bibliothèque à part).

Est-ce possible ? Existe-t-il un manipulateur qui permet d'inhiber,
temporairement, la locale courante ?


Pas tout fait, mais c'est facile d'en faire. La fonction imbue()
renvoie le locale dont se servait du flux avant ; il y a aussi
une fonction getloc() pour le lire sans y toucher. Dans plop,
donc :

void
plop( std::ostream& os )
{
std::locale saved = os.getloc() ;
os.imbue( std::locale::classic() ) ;
os << 123456 << std::endl ;
os.imbue( saved ) ;
}

(Sauf qu'évidemment, on se servira du RAII pour la restauration
de l'ancien état.)

Néaumoins, j'y serais très méfiant. Modifier le locale du flux
modifie aussi le locale du filebuf dont il se sert ; imbue sur
le flux appelle imbue sur le streambuf, et si ce streambuf est
un filebuf, on a la précondition : « If the file is not
positioned at its beginning and the encoding of the current
locale as determined by a_codecvt .encoding() is state-dependent
(22.2.1.4.2) then that facet is the same as the corresponding
facet of loc. ». S'il y a une risque d'un encodage qui dépend
de l'état (et selon la façon que c'est implémenté, UTF-8
pourrait en être un), il faut un étap en plus :

void
plop( std::ostream& os )
{
std::locale saved = os.getloc() ;
os.imbue( std::locale( std::locale::classic(),
saved,
std::locale::ctype ) ;
os << 123456 << std::endl ;
os.imbue( saved ) ;
}

Un autre alternatif, c'est de formatter dans un
std::ostringstream, et puis simplement sortir la chaîne ainsi
générée :

void
plop( std::ostream& os )
{
std::ostringstream tmp ;
tmp.imbue( std::locale::classic() ) ;
tmp << 123456 ;
os << tmp.rdbuf() ;
}

Ça a aussi l'avantage (ou désavantage, selon ce qu'on veut) de
te rendre indépendant des paramètres de formattage actif lors de
l'appel. Donc, si dans main, l'utilisateur a fait
std::cout << std::hex, en tripotant le locale, tu sors encore
en hexadécimal, tandis qu'avec le stringstream temporaire, tes
sorties sont en décimal, quelque soit le formattage actuellement
en vigueur dans la fonction appelante.

Il me semble qu'il doit être possible de la changer, puis de
rétablir l'originale ensuite, mais ce n'est pas trop ce que je
cherche...


C'est cependant comme ça que ça marche.

En passant, à part les nombres (entiers ou flottants), quels types
sont susceptibles d'êtres "localisés" en sortie ?


Selon ce que tu entends par « localisés » : l'encodage du
fichier dépend du locale (y compris en mode binary !). Donc,
tous les octets que tu sors sont « localisés ». Sinon, qui
sait : la norme ne prévoit que le formattage des types
arithmétiques, des pointeurs et des chaînes de caractères. Le
format des types arithmétiques dépend toujours du locale, celui
des pointeurs, selon l'implémentatoin, et en fait, les chaînes
ne sont pas formattées, quelque soit le locale. Mais la norme ne
dit rien sur des types utilisateurs (sauf std::complex, qui
n'est pas localisé !), qui en font la plupart des sorties. Il
faut donc chaque fois régarder la documentation. (Je
m'attendrais, par exemple, que boost::date_time ou diverses
classes monétaires soient localisées.)

--
James Kanze (GABI Software) email:
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
Vincent Richard
James Kanze wrote:

Un autre alternatif, c'est de formatter dans un
std::ostringstream, et puis simplement sortir la chaîne
ainsi générée


Merci, c'est la solution que j'ai retenue, même si ça fait
un peu "marteau pour tuer une mouche".

Une petite question à ce propos : je suppose que ça dépend des
implémentations, mais en général qu'est-ce que la construction
d'un ostringstream induit (réservation de mémoire, etc.) ?

Est-ce que les implémentations sont optimisées pour ne pas trop
pénaliser la création de ces objets (du point de vue temps CPU,
mais aussi consommation mémoire) ?

Vincent

Avatar
James Kanze
Vincent Richard wrote:
James Kanze wrote:

Un autre alternatif, c'est de formatter dans un
std::ostringstream, et puis simplement sortir la chaîne
ainsi générée


Merci, c'est la solution que j'ai retenue, même si ça fait
un peu "marteau pour tuer une mouche".


Il ne doit pas l'être. En général, [io]stringstream doit être la
solution « habituelle » pour convertir de et vers std::string.
D'autant plus quand tu as des exigeances particulières de
formattage.

Une petite question à ce propos : je suppose que ça dépend des
implémentations, mais en général qu'est-ce que la construction
d'un ostringstream induit (réservation de mémoire, etc.) ?

Est-ce que les implémentations sont optimisées pour ne pas trop
pénaliser la création de ces objets (du point de vue temps CPU,
mais aussi consommation mémoire) ?


À vrai dire, je ne sais pas. A priori, il n'y a aucune raison
pour qu'elle reserve de la mémoire avant d'en avoir besoin, pour
la chaîne même. Mais traditionnellement, les implémenteurs
semblent avoir fait un maximum pour rendre les iostream aussi
lourds que possible.

--
James Kanze (GABI Software) email:
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