Internacionalizace a lokalizace v C++
V p°edchozφch dφlech Φlßnku v∞novanΘho problematice lokalizace, resp. internacionalizace program∙ v C++ jsme se seznßmili s t°φdou std::locale a s fazetami, nauΦili jsme se Φφst a zapisovat Φesk² text v r∙zn²ch k≤dovßnφch, p°evßd∞t malß pφsmena na velkß a naopak a °adit znakovΘ °et∞zce podle abecedy. V poslednφm dφlu se podφvßme na formßtovßnφ Φφsel.
Ka₧d² zaΦφnajφcφ programßtor se uΦφ, ₧e v reßln²ch Φφslech musφ v programech psßt desetinnou teΦku, nikoli Φßrku, a ₧e nesmφ pou₧φvat mezery pro odd∞lovßnφ tisφc∙. Jen₧e to je vlastn∞ nep°irozenΘ: celß kontinentßlnφ Evropa pou₧φvß desetinnou Φßrku a n∞jakΘ odd∞lovaΦe tisφc∙. Podφvßme-li se za hranice Evropy, zjistφme, ₧e jinΘ nßrody seskupujφ Φφslice ve vφcecifern²ch Φφslech i ji.nak ne₧ po t°ech (tak₧e vlastn∞ ani nelze hovo°it o odd∞lovaΦφch tisφc∙).
Pro nßs je p°irozenΘ napsat Φφslo mili≤n 1 000 000,00,
zatφmco N∞mec napφÜe 1.000.000,00 a AngliΦan pro
zm∞nu 1,000,000.00. V Nepßlu pr² totΘ₧ Φφslo zapφÜφ
jako 10.00.000,00.
Navφc se tuto zvyklosti mohou v pr∙b∞hu Φasu vyvφjet, tak₧e je jasnΘ, ₧e i
tyto odliÜnosti nelze dost dob°e naprogramovat jednou pro v₧dy, ale ₧e je
t°eba je naΦerpat stejn∞ jako jinΘ souΦßsti nßrodnφho nastavenφ nap°. z dat
ulo₧en²ch v operaΦnφho systΘmu û tak, jako to d∞lß zpravidla t°φda locale.
O formßtovßnφ Φφsel se starajφ fazety numpunct, num_get a num_put.
- Fazeta numpunct definuje znak pro desetinnou Φßrku (nebo teΦku), odd∞lovaΦe tisφc∙ a jmΘna pro logickΘ hodnoty.
- Fazeta num_get se starß o anal²zu vstupnφho °et∞zce, kter² obsahuje Φφslo.
- Fazeta num_put mß na starosti formßtovßnφ vystupujφcφho °et∞zce, kter² p°edstavuje Φφselnou hodnotu.
Fazety num_get a num_put pou₧φvajφ fazetu numpunct.
Implicitnφ nastavenφ
Podφvejme se, jak dopadne v²stup celΘho a reßlnΘho Φφsla v ΦeskΘm nßrodnφm
nastavenφ tak, jak je k dispozici ve Visual C++ .NET pod Vindows 2000. Vezm∞me
nßsledujφcφ jednoduch² program:
// Test ΦeskΘho nßrodnφho nastavenφ
// pro celß a reßlnß Φφsla
#include <locale>
#include <iostream>
using namespace std;
int main()
{
locale L("Czech_Czech Republic.852");
double d = 123456.7;
int i = 100000;
wcout << fixed << boolalpha;
wcout << L"implicitni nastaveni: " << d << endl
<< i << endl << true << endl;
wcout.imbue(L);
wcout << L"ΦeskΘ nastavenφ: " << d << endl << i << endl
<< true << endl;
return 0;
}
Zde nejprve vypφÜeme reßlnΘ a celΘ Φφslo s implicitnφm nastavenφm a pak s Φesk²m nastavenφm. V²stup tohoto programu bude
implicitni nastaveni: 123456.700000
1000000
true
ΦeskΘ nastavenφ: 123456,700000
1000000
true
Je tedy z°ejmΘ, ₧e vÜe, Φeho jsme dosßhli, je, ₧e se ve v²stupu reßlnΘho Φφsla objevila desetinnß Φßrka. (Tu nynφ musφme pou₧φt i p°i vstupu.) Implicitnφ nastavenφ neobsahuje odd∞lovaΦe tisφc∙ a Φeskß jmΘna pro logickΘ hodnoty. Pokud opravdu trvßme na odd∞lovaΦφch tisφc∙, musφme si je naprogramovat, a podobn∞ pokud chceme pou₧φvat jinß jmΘna pro hodnoty logick²ch konstant true a false.
Fazeta numpunct
Cesta ke zm∞n∞ formßtovßnφ p°i vstupnφch a v²stupnφch operacφch v datov²ch
proudech vede p°es fazetu std::numpunct<>.
Ve standardnφ knihovn∞ jazyka C++ je definovßna takto:
namespace std {
template <class charT>
class numpunct : public locale::facet {
public:
typedef charT char_type;
typedef basic_string<charT> string_type;
explicit numpunct(size_t refs = 0);
char_type decimal_point() const;
char_type thousands_sep() const;
string grouping() const;
string_type truename() const;
string_type falsename() const;
static locale::id id;
protected:
~numpunct(); //virtußlnφ
virtual char_type do_decimal_point() const;
virtual char_type do_thousands_sep() const;
virtual string do_grouping() const;
virtual string_type do_truename() const; // pro typ bool
virtual string_type do_falsename() const; // pro typ bool
};
}
èablonov² parametr charT znamenß jako obvykle znakov² typ, tedy char nebo wchar_t. Metody decimal_point(), thousands_sep(), truename() a falsename() vracejφ po °ad∞ znak pro desetinnou Φßrku, odd∞lovaΦ tisφc∙, znakov² °et∞zec pro hodnotu true a znakov² °et∞zec pro hodnotu false. Prvnφ dv∞ metody vracejφ hodnotu typu charT, poslednφ dv∞ vracejφ metodu typu basic_string<charT>. (To znamenß, ₧e typ vracenΘ hodnoty se tedy liÜφ podle toho, zda pracujeme s ·zk²mi nebo se Üirok²mi znaky.)
Metoda grouping() vracφ znakov² °et∞zec typu string (v₧dy ·zkΘ znaky, tedy typ basic_string<char>) kter² urΦuje rozd∞lenφ Φφslic v cel²ch Φφslech do skupin. Vrßtφme se k nφ v nßsledujφcφm odstavci.
Tyto metody ned∞lajφ nic jinΘho, ne₧ ₧e volajφ odpovφdajφcφ virtußlnφ metody, pojmenovanΘ do_decimal_point(), do_thousands_sep(), do_grouping(), do_truename() a do_falsename(). To umo₧≥uje pou₧φt na mφst∞ fazety numpunct û tedy na mφst∞ instance t°φdy numpunct û instanci jakΘkoli odvozenΘ t°φdy.
Statickß slo₧ka id typu std::locale::id slou₧φ k identifikaci fazety; pou₧φvß se intern∞ jako klφΦ p°i vyhledßvßnφ fazety v instanci t°φdy locale. V potomkovi t°φdy std::numpunct ji nenφ t°eba definovat. (P°esn∞ji, nesmφme ji definovat znovu, jinak by ji t°φda locale chßpala jako jin² typ fazety.)
Skupiny Φφslic
U₧ vφme, ₧e °et∞zec vrßcen² metodou grouping()
urΦuje zp∙sob seskupovßnφ Φφslic. Jestli₧e nechceme Φφslice nijak seskupovat,
zadßme prßzdn² °et∞zec "" nebo "\0".
Pro nejb∞₧n∞jÜφ zp∙sob, seskupovßnφ po t°ech, zadßme "\3".
Pokud bychom si p°ßli nejprve skupinu 4 Φφslic, pak skupinu dvou a nakonec
skupinu 3 Φφslic, zadali bychom °et∞zec "\4\2\3".
P°esnΘ pravidlo je jednoduchΘ: zadan² znakov² °et∞zec se bere jako pole Φφsel. ╚φselnß hodnota prvnφho prvku udßvß velikost prvnφ skupiny (skupiny nejni₧Üφch Φφslic), Φφselnß hodnota druhΘho prvku udßvß velikost druhΘ skupiny atd. Je-li Φφslo p°φliÜ velkΘ a odpovφdajφcφ skupina nenφ °et∞zcem vrßcen²m funkcφ grouping() popsßna, pou₧ije se pro ni velikost poslednφ skupiny. To znamenß, ₧e poslednφ velikost skupiny se stßle opakuje.
P°φklad
NapφÜeme si vlastnφ fazetu pro formßtovßnφ Φφsel. Nßsledujφcφ program
ukazuje, ₧e formßtovßnφ se uplatnφ nejen p°i v²stupu, ale i p°i Φtenφ:
// Definice a pou₧itφ vlastnφ fazety
// pro formßtovßnφ Φφsel a logick²ch hodnot
#include <iostream>
#include <stdexcept>
#include <fstream>
#include <locale>
using namespace std;
class punkt_w: public std::numpunct<wchar_t> {
public:
typedef wchar_t char_type;
typedef wstring string_type;
explicit punkt_w(size_t r = 0): std::numpunct<wchar_t>(r){}
protected:
char_type do_decimal_point() const {return ',';}
char_type do_thousands_sep() const {return ' ';}
string do_grouping() const {return "\003";}
string_type do_truename() const {return L"pravda";};
string_type do_falsename() const {return L"nepravda";};
};wchar_t cw[] = L"Älu¥ouΦk² k∙≥ p°φÜern∞ ·p∞l ∩ßbelskΘ ≤dy.";
int main()
{
try{
locale Cr("Czech_Czech Republic.852");
locale Kon(Cr, new punkt_w(0));
wcout.imbue(Kon);
wcin.imbue(Kon);
wcout << cw << endl;
bool b = true;
int n = 12345;
double d = 12.345678;
wcin >> boolalpha;
wcout << boolalpha << b << endl;
wcout << n << endl;
wcout << d << endl;
wcout << L"zadej reßlnΘ Φφslo: ";
wcin >> d;
wcout << L"dvojnßsobek je "<< d*2 << endl;
wcout << L"zadej celΘ Φφslo: ";
wcin >> n;
wcout << L"dvojnßsobek je "<< 2*n << endl;
wcout << L"zadej logickou hodnotu: ";
wcin >> b;
wcout << L"Opak je " << !b << endl;
}
catch(runtime_error &e)
{
cerr << e.what() << endl;
}
return 0;
}
Fazetu punct jsme odvodili jako potomka fazety std::numpunct<wchar_t> (pouze pro ÜirokΘ znaky). V nφ jsme definovali mezeru jako odd∞lovaΦ tisφc∙, Φßrku jako desetinnou teΦku a °et∞zce pravda, resp. nepravda jako slovnφ reprezentaci hodnot true, resp. false.
Na poΦßtku funkce main() vytvo°φme instanci Cr t°φdy locale reprezentujφcφ ΦeskΘ nßrodnφ nastavenφ; pak vytvo°φme instanci Kon, kterß p°evezme z instance Cr vÜechny fazety krom∞ numpunct; tu nahradφ instancφ fazety punct.
Abychom se p°esv∞dΦili, ₧e instance Kon opravdu p°evzala ostatnφ fazety z instance Cr, vypφÜeme nejprve oblφbenou v∞tu o ₧lu¥ouΦkΘm koni.
Pak pomocφ manipulßtoru boolalpha p°edepφÜeme, ₧e po₧adujeme vstup a v²stup logick²ch hodnot ve form∞ znakovΘho °et∞zce, nikoli v podob∞ Φφsel 0 nebo 1.
Konverzace s tφmto programem m∙₧e vypadat takto:
Älu¥ouΦk² k∙≥ p°φÜern∞ ·p∞l ∩ßbelskΘ ≤dy.
pravda
12 345
12,3457
zadej reßlnΘ Φφslo: 3,1415926
dvojnßsobek je 6,28319
zadej celΘ Φφslo: 1 000 000 000
dvojnßsobek je 2 000 000 000
zadej logickou hodnotu: nepravda
Opak je pravda
Dopl≥me, ₧e p°i zadßvßnφ cel²ch Φφsel m∙₧eme mezery, p°edstavujφcφ odd∞lovaΦe tisφc∙, vynechat. (Musφme ale bu∩ vÜechny vynechat, nebo vÜechny uvΘst, jinak se Φφslo sprßvn∞ nep°eΦte. Nesmφme takΘ nikde pou₧φt vφce ne₧ jednu mezeru.)
Zm∞nφme-li °et∞zec, vracen² metodou do_grouping(), na "\4\2\3", vypφÜe se Φφslo 2000000000 ve tvaru
2 000 00 0000
VÜimn∞te si, ₧e seskupovßnφ a odd∞lovaΦe tisφc∙ se nepou₧ily pro reßlnß Φφsla. To je obecnΘ pravidlo: fazeta numpunct urΦuje pouze desetinnou Φßrku (teΦku), nikoli seskupovßnφ a odd∞lovaΦe Φφslic. Pokud bychom n∞co takovΘho cht∞li, musφme si to naprogramovat sami.
Poznamenejme, ₧e kdybychom jako °et∞zec, vracen² funkcφ do_grouping(), mφsto "\3"pou₧ili "3", seskupovßnφ Φφslic v cel²ch Φφslech by se neuplatnilo. Znak '3' toti₧ p°edstavuje Φφselnou hodnotu 51, tak₧e by se program pokusil vytvo°it skupiny po 51 Φφslicφch.
Poznßmka
Formßtovßnφ cel²ch Φφsel, kterΘ jsme zde definovali, se uplatnφ pouze pro
proudy pou₧φvajφcφ ÜirokΘ znaky. Budeme-li chtφt podobn²m zp∙sobem formßtovat
vstup a v²stup Φφsel a logick²ch hodnot pro ·zkΘ znaky, budeme si muset napsat
podobnou fazetu i pro typ char.
Fazetu punct se tedy vyplatφ deklarovat jako Üablonu s parametrem, kter² bude urΦovat pou₧it² znakov² typ. P°itom ovÜem narazφme na problΘm se znakov²mi literßly; souΦasnΘ C++ toti₧ nenabφzφ elegantnφ zp∙sob, jak definovat °et∞zcov² literßl, kter² by byl v zßvislosti na parametru Üablony typu char nebo wchar_t. Jedno z mo₧n²ch °eÜenφ je pou₧φt statickß znakovß pole a inicializovat je prvek po prvku, nebo¥ pro typ char je definovßna implicitnφ konverze na wchar_t:
template<typename charT>
class punkt: public std::numpunct<charT> {
public:
typedef charT char_type;
typedef basic_string<charT> string_type;
explicit punkt(size_t r = 0): std::numpunct<charT>(r){}
protected:
char_type do_decimal_point() const {return ',';}
char_type do_thousands_sep() const {return ' ';}
string do_grouping() const {return "\003";}
string_type do_truename() const {return string_type(true_name);};
string_type do_falsename() const {return string_type(false_name);};
private:
static char_type true_name[];
static char_type false_name[];
};template<class charT>
punkt<charT>::char_type punkt<charT>::true_name[] =
{'p','r','a','v','d','a','\0'};template<class charT>
punkt<charT>::char_type punkt<charT>::false_name[] =
{'n','e','p','r','a','v','d','a','\0'};
ZaΦßtek funkce main() pak upravφme nßsledujφcφm zp∙sobem:
int main()
{
try{
locale Cr("Czech_Czech Republic.852");
locale Kon0(Cr, new punkt<wchar_t>(0));
locale Kon(Kon0, new punkt<char>(0));
wcout.imbue(Kon);
wcin.imbue(Kon);
cout.imbue(Kon);
// ... a dßle jako p°edtφm
}
Fazety p°idßvßme do instance t°φdy locale jednu po druhΘ.
Nynφ si ukß₧eme, jak lze definovat a pou₧φvat vlastnφ fazetu. Jde o upraven² p°φklad z dodatku D knihy [1].
Definujeme fazetu, kterß bude slou₧it ke vstupu a v²stupu nßzv∙ roΦnφch
obdobφ a kterou p∙jde pou₧φt jen pro ·zkΘ znaky. Pro ÜirokΘ znaku je postup
podobn².
Nejprve definujeme v²Φtov² typ popisujφcφ roΦnφ obdobφ:
enum obdobi {jaro, leto, podzim, zima};
Je-li s prom∞nnß typu obdobi obsahujφcφ hodnotu podzim, m∞l by nßÜ program po zadßnφ p°φkazu
cout << s;
vypsat °et∞zec podzim. Podobn∞ p°i vstupu dat tohoto typu chceme zadßvat °et∞zce p°erdstavujφcφ jmΘna roΦnφch obdobφ, nikoli Φφsla.
Fazeta Obd_vv
Na prvnφ pohled se m∙₧e zdßt, ₧e k tomu staΦφ p°etφ₧it v hodn²m zp∙sobem operßtory
<< a >>,
nenφ t°eba zat∞₧ovat se s fazetami nßrodnφho nastavenφ.
To je pravda, pokud nßm jde o jeden program. Pokud bychom ovÜem cht∞li upravit pou₧φvßnφ typu obdobi v ÜirÜφm kontextu û nap°. ve vÜech p°ekladaΦφch, a pokud bychom cht∞li umo₧nit pou₧φvßnφ °et∞zc∙ k prßci s roΦnφmi obdobφmi i v jin²ch nßrodnφch prost°edφch, mß smysl definovat pro tento ·Φel fazetu.
Rozhodneme se tedy, ₧e definujeme fazetu Obd_vv (vstup a v²stup roΦnφho obdobφ). Abychom umo₧nili dalÜφm programßtor∙m vytvo°it odpovφdajφcφ fazety pro jinß nßrodnφ nastavenφ, bude rozumnΘ definovat Obd_vv jako abstraktnφ t°φdu a od nφ odvodit fazetu pro ΦeskΘ nßrodnφ nastavenφ:
class Obd_vv: public locale::facet {/* ... */ }; // abstraktnφ fazeta
class CZ_Obd_vv: public Obd_vv{/* ... */ }; // Φeskß verze
P°etφ₧enΘ operßtory << a >> budou se budou odvolßvat na abstraktnφ t°φdu Obd_vv. To znamenß, ₧e veÜkerΘ slu₧by, kterΘ tato t°φda bude poskytovat, musφ b²t implementovßny jako virtußlnφ metody.
Fazeta pro roΦnφ obdobφ musφ um∞t konvertovat hodnotu v²ΦtovΘho typu na znakov² °et∞zec a znakov² °et∞zec na hodnotu v²ΦtovΘho typu. P°esn∞ji, musφ um∞t konvertovat alespo≥ hodnoty odpovφdajφcφ n∞kterΘ z v²Φtov²ch konstant. Proto bude obsahovat metody, kterΘ vtipn∞ pojmenujeme to_str() a from_str():
class Obd_vv: public locale::facet {
public:
Obd_vv(int i = 0): locale::facet(i){}
~Obd_vv(){}
virtual const string& to_str(obdobi x) const = 0
virtual bool from_str(const string& s, obdobi& x) const = 0;
static locale::id id;
};
locale::id Obd_vv::id;
Metoda from_str() vracφ logickou hodnotu indikujφcφ,
zda se zadanou hodnotu poda°ilo konvertovat, tj. zda x
obsahuje hodnotu odpovφdajφcφ n∞kterΘ z konstant.
P°ipome≥me si, ₧e tato t°φda musφ obsahovat statickou slo₧ku id typu std::locale::id,
kterou bude t°φda locale pou₧φvat k jejφ identifikaci.
Od t°φdy Obd_vv odvodφme implementaci pro ΦeskΘ nastavenφ, kterou nazveme nap°. CZ_Obd_vv:
class CZ_Obd_vv: public Obd_vv
{
static const string nazvy[];
public:
const string& to_str(obdobi) const;
bool from_str(const string&, obdobi&) const;
};
const string CZ_Obd_vv::nazvy[] = {"jaro","leto","podzim","zima"};
v nφ₧ budou ji₧ definovßny metody to_str() a from_str(). Dßle ovÜem musφme p°etφ₧it operßtory << a >>, aby p°i v²stupu vyu₧φvaly slu₧eb tΘto fazety. Podφvejme se nynφ na cel² program:
// Definice a pou₧itφ vlastnφ fazety
// pro formßtovßnφ Φφsel a logick²ch hodnot
#include <iostream>
#include <stdexcept>
#include <fstream>
#include <locale>
#include <algorithm>
using namespace std;
enum obdobi {jaro, leto, podzim, zima};
class Obd_vv: public locale::facet { // Abstraktnφ p°edek
public:
Obd_vv(int i = 0): locale::facet(i){}
~Obd_vv(){}
virtual const string& to_str(obdobi x) const = 0;//{static const string s("");return s;};
virtual bool from_str(const string& s, obdobi& x) const = 0; //{return true;}
static locale::id id;
};
locale::id Obd_vv::id;
class CZ_Obd_vv: public Obd_vv // Implementace pro ΦeskΘ
{ // nastavenφ
static const string nazvy[];
public:
const string& to_str(obdobi) const;
bool from_str(const string&, obdobi&) const;
//CZ_Obd_vv(int i = 0): Obd_vv(i){}
};
const string CZ_Obd_vv::nazvy[] = {"jaro","lΘto","podzim","zima"};
// Implementace metod pro p°evod na °et∞zec a z n∞j
const string& CZ_Obd_vv::to_str(obdobi x) const
{
static const string s("nesprßvnΘ obdobφ");
if(x < jaro || x > zima) return s;
else return nazvy[x];
}
bool CZ_Obd_vv::from_str(const string& s, obdobi& x) const
{
const string* p = find<const string*, string>(nazvy, &nazvy[zima+1], s);
if(p == &nazvy[zima+1]) return false;
x = obdobi(p - nazvy);
return true;
}
// P°etφ₧enΘ operßtory pro vstup a v²stup,
// kterΘ vyu₧φvajφ slu₧eb fazety Obd_vv
ostream& operator<<(ostream& Proud, obdobi x)
{
const locale& loc = Proud.getloc();
if(has_facet<Obd_vv>(loc))
return Proud << use_facet<Obd_vv>(loc).to_str(x);
else
return Proud << int(x);
};
istream& operator>>(istream& Proud, obdobi &x)
{
const locale &loc = Proud.getloc();
if(has_facet<Obd_vv>(loc)){
const Obd_vv& f = use_facet<Obd_vv>(loc);
string buf;
if(!(Proud >> buf && f.from_str(buf, x)))
Proud.setstate(ios_base::failbit);
return Proud;
}
int i;
Proud >> i;
x = obdobi(i);
return Proud;
}
int main()
{
try{
locale Cr("Czech_Czech Republic.852");
locale Kon(Cr, new CZ_Obd_vv);
obdobi ob = leto;
cout.imbue(Kon);
cin.imbue(Kon);
cout << ob << endl;
cin >> ob;
cout << int(ob) << endl;
}
catch(runtime_error &e)
{
cerr << e.what() << endl;
}
return 0;
}
Implementace metody to_str() pro p°evod hodnoty typu obdobi na znakov² °et∞zec je p°φmoΦarß a nevy₧aduje komentß°e. Implementace metody from_str() je jen nepatrn∞ slo₧it∞jÜφ. K vyhledßnφ nßzvu s v poli nßzv∙ roΦnφch obdobφ pou₧φvß funkce find<>(), jejφ₧ Üablona je v hlaviΦkovΘm souboru <algorithm>.
const string* p = find<const string*, string>(nazvy, &nazvy[zima+1], s);
Prvnφ parametr udßvß ukazatel na prvnφ prvek prohledßvanΘho ·seku, druh² udßvß ukazatel za poslednφ prvek a t°etφ je hledanß hodnota. Tato funkce vrßtφ ukazatel na nalezen² °et∞zec nebo ukazatel na poslednφ prvek, pokud dan² °et∞zec nenajde. P°φkaz
x = obdobi(p - nazvy);
zjistφ pomocφ adresovΘ aritmetiky index nalezenΘho prvku, p°etypuje ho na obdobi a ulo₧φ ho do prom∞nnΘ x, v nφ₧ se vracφ v²sledek.
Implementace operßtoru << pro v²stup je op∞t pom∞rn∞ p°φmoΦarß. Tato funkce nejprve zφskß odkaz na instanci t°φdy locale datovΘho proudu,
const locale& loc = Proud.getloc();
Pak si zjistφ, zda tato instance obsahuje fazetu Obd_vv. Pokud ano, pou₧ije jejφ metodu to_str() k p°evodu na hodnoty typu obdobi °et∞zec, jinak vypφÜe zadanou hodnotu jako Φφslo. P°etypovßnφ na int v p°φkazu
return Proud << int(x);
je nezbytnΘ, nebo¥ jinak by se rekurzivn∞ volal operßtor << pro typ obdobi.
Implementace operßtoru >> pro vstup je pon∞kud slo₧it∞jÜφ. TakΘ tato funkce nejprve zφskß odkaz na instanci t°φdy locale datovΘho proudu a zjistφ, zda obsahuje pot°ebnou fazetu. Pokud ne, p°eΦte prost∞ celΘ Φφslo, p°etypuje ho na obdobi a v²sledek ulo₧φ do parametru x. Pokud ovÜem instance t°φdy locale pot°ebnou fazetu obsahuje, p°eΦte z proudu znakov² °et∞zec a pokusφ se ho p°evΘst na typ obdobi pomocφ metody from_str() naÜφ fazety; v²sledek ulo₧φ do parametru x. Jestli₧e p°i Φtenφ nebo p°i p°evodu °et∞zce neusp∞je, nastavφ p°φznak chyby v proudu.
Poznßmka
Budete/li si tento p°φklad zkouÜet, m∙₧ete narazit na nßsledujφcφ problΘm:
N∞kterΘ starÜφ implementace standardnφ knihovny jazyka C++ (nap°. Borland
C++Builder 4 a 5 nebo Microsoft Visual C++ 6) si z n∞jak²ch zßhadn²ch d∙vod∙
vytvß°ejφ takΘ instanci p°edka pou₧itΘ fazety. V naÜem p°φpad∞ se tedy pokusφ
vytvo°it instanci t°φdy Obd_vv; pochopiteln∞ ohlßsφ
chybu, ₧e nemohou vytvo°it instanci abstraktnφ t°φdy. Pomoc je snadnß, staΦφ
abstraktnφm dßt metodßm implementaci û staΦφ jakßkoli, nebo¥ nebudou pou₧ity.
class Obd_vv: public locale::facet { // Kdy₧ nesmφ b²t abstraktnφ
public:
Obd_vv(int i = 0): locale::facet(i){}
~Obd_vv(){}
virtual const string& to_str(obdobi x) const {
static const string s("");
return s;
}
virtual bool from_str(const string& s, obdobi& x){ return true; }
static locale::id id;
};
Na zßv∞r je t°eba jeÜt∞ jednou zd∙raznit, ₧e definice vlastnφ fazety je n∞co, co do b∞₧nΘho programu nepat°φ; samotnΘ p°etφ₧enφ operßtor∙ >> a << obstarß totΘ₧ jednoduÜeji.
I kdy₧ naÜe povφdßnφ o nßrodnφm prost°edφ m∞lo Φty°i dφly, nepokr²vß vÜe, co by o tΘto problematice stßlo za napsßnφ. Nehovo°ili jsme nap°φklad o formßtovßnφ datov²ch a Φasov²ch ·daj∙, o formßtovßnφ m∞nov²ch ·daj∙ nebo o prßci s katalogem zprßv. I kdy₧ jde o v∞ci na prvnφ pohled pot°ebnΘ, prßce s nimi nenφ tak snadnß jako prßce s °et∞zci nebo s Φφsly. Jazyk C++ toti₧ neobsahuje standardnφ t°φdu Date (nebo jinou, kterß by se hodila k prßci s Φasov²mi ·daji), stejn∞ jako neobsahuje nap°. t°φdu pro reprezentaci pen∞₧nφch Φßstek. Pokud n∞co takovΘho chceme, musφme si to naprogramovat sami. Nehovo°ili jsme takΘ o prßci s nßrodnφm prost°edφm v jazyce C.
Podobn∞ jako v p°edchozφch dφlech, i zde musφm Φtenß°e upozornit, ₧e souΦasnΘ p°ekladaΦe s nßrodnφm nastavenφm nezachßzejφ p°φliÜ korektn∞. Nebu∩te tedy p°ekvapeni, kdy₧ uvedenΘ p°φkladu nebudou ve vaÜem p°ekladaΦi fungovat.
V mnoha starÜφch p°ekladaΦφch narazφme na problΘmy, je₧ pochßzejφ ze skuteΦnosti, ₧e tyto p°ekladaΦe neimplementujφ vno°enΘ Üablony, neum∞jφ pou₧φt jeden parametr Üablony jako implicitnφ hodnotu nßsledujφcφho parametru, nepodporujφ explicitnφ specifikaci parametry Üablony u ÜablonovΘ funkce atd. Mφsto funkcφ use_face<>() a has_face<>() pak musφme pou₧φvat r∙znß makra.
V∞tÜina souΦasn²ch b∞₧n∞ rozÜφ°en²ch p°ekladaΦ∙ si takΘ neporadφ s p°ekladem
·zk²ch znak∙ z jednoho k≤dovßnφ do jinΘho; proto jsem v n∞kter²ch p°φkladech
pou₧φval pouze ÜirokΘ znaky. (Ostatn∞ n∞kterΘ si neporadφ ani s p°ekladem
Üirok²ch znak∙, pokud obsahujφ znaky nßrodnφ abecedy û a je jedno, zda jde
o abecedu Φeskou, polskou, n∞meckou nebo francouzskou).
N∞kterΘ p°ekladaΦe takΘ nekorektn∞ zachßzejφ s p°edky fazetov²ch t°φd (o tom
jsme mluvili v tomto pokraΦovßnφ v oddφlu v∞novanΘm vlastnφ fazet∞).
VÜechny p°φklady zde uvedenΘ ve vÜech dφlech tohoto Φlßnku jsem p°elo₧il a vyzkouÜel ve Visual C++ .NET. Ani tento p°ekladaΦ se ovÜem nechovß zcela korektn∞ (nezvlßdß p°ek≤dovßnφ ·zk²ch znak∙, jestli₧e obsahujφ znaky nßrodnφch abeced).
Problematice nßrodnφho nastavenφ se v∞nujφ auto°i knih o C++ spφÜe v²jimeΦn∞. AmeriΦany a nangliΦany to p°φliÜ nezajφmß, nebo¥ poΦφtaΦe hovo°φ p°evß₧n∞ jejich jazykem, a t∞ch n∞kolik drobnostφ, v nich₧ se odchylujφ, v∞tÜinou nep∙sobφ problΘmu. Je p°φznaΦnΘ, ₧e prvnφ podrobn∞jÜφ zmφnky o t°φd∞ locale jsem naÜel v knize n∞meckΘho autora. (V mnoha ohledech Ülo ovÜem o knihu velmi Üpatnou, a proto ji zde neuvßdφm.)
Podrobn² v²klad lze najφt v anglicky psanΘ knize [2], jejφm₧ autorem je takΘ N∞mec.
Dobr²m zdrojem informacφ je takΘ kniha B. Stroustrupa [1]. (Autor je Dßn.) V dodatku D najdete rozsßhlou kapitolu v∞novanou tΘto problematice. Musφ ovÜem jφt o pozd∞jÜφ tisky t°etφho vydßnφ nebo o vydßnφ oznaΦenΘ Special Edition. DruhΘ vydßnφ, kterΘ bylo p°elo₧eno do ΦeÜtiny, tyto informace neobsahuje. Neobsahujφ je ani prvnφ tisku t°etφho vydßnφ z r. 1997; lze si je ale stßhnout ve formßtu PDF z adresy http://www.research.att.com/~bs/3rd_loc.pdf.
Miroslav Virius
[1] B. Stroustrup: The C++ Programming
Language. Addison-Wesley 2000. Dodatek D. (Pouze v nov∞jÜφch tiscφch
3. vydßnφ a ve vydßnφ oznaΦenΘm Special Edition. Lze takΘ stßhnout ve formßtu
PDF ze strßnky http://www.research.att.com/~bs/3rd_loc.pdf.
[2] N. M. Josuttis: The C++ Standard
Library. A Tutorial and Reference. Addison-Wesley 2000.
Pokud vßs problematika Internacionalizace a lokalizace v C++ zaujala, napiÜte nßm na adresu chipcd@vogel.cz o jejφ pokraΦovßnφ. OΦekßvßme takΘ vaÜe nßm∞ty a p°ipomφnky.