Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

Napsat správně mezinárodní aplikaci v Qt nemusí být snadné

Dobře napsaná aplikace – to je kus umění. Přesvědčit vás o tom může i okamžik, kdy se vaše aplikace dostane mezinárodnímu publiku. Rázem je nám osmibitový char těsný a uživatelé si stěžují, že soubor používající německou diakritiku nebo norské znaky nejde otevřít a že je potřeba s tím něco dělat.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

Unicode má, zjednodušeně řečeno, množství různých kódování. Nejznámější jsou tato:

kódování nativní na platformě datový typ zapouzdřující třídy
UTF-8 Linux char std::string
UTF-16 Windows Windows: wchar_t std::wstring (Windows), QString
UTF-32 Linux: wchar_t std::wstring (Linux)

UTF-8

Nejrozšířenější kódování na Linuxu je UTF-8. Používá standardní typ char, přičemž znaky nad 127 jsou reprezentovány vícebajtovou hodnotou. Jeden znak může tímto způsobem obsadit 1 až 6 bajtů. Jednou z podstatných výhod tohoto kódování je, že Linux nepotřebuje nové API, aby podporoval unicode. Standardní funkci fopen pouze předáte UTF-8 zakódovaný název souboru a daný soubor bude pro vás otevřen. Název souboru přitom může být třeba v japonštině.

Text v UTF-8 můžeme snadno obsluhovat jako standardní nulou ukončený řetězec nebo jej uložit do std::string. Je zde však pár věcí, na které je nutno pamatovat. std::string sice krásně reprezentuje náš UTF-8 text, ale při zavolání funkce length() nedostaneme skutečný počet znaků, ale pouze množství bajtů, které daný text v paměti zabírá. Totéž dělá funkce strlen – vrací počet bajtů, které daný text v paměti zabírá. To reflektuje časté použití funkce strlen ke zjištění, kolik paměti je třeba pro daný text alokovat. Pro zjištění skutečného počtu znaků můžeme použít například funkci mbstowcs().

UTF-16

UTF-16 je nativně používané uvnitř Windows. I když používáte staré 8-bitové ASCII/ANSI kódování s různými znakovými sadami (code pages), kdykoliv tento text předáte Windows, on si jej zkonvertuje na UTF-16 před jakýmkoliv dalším zpracováním. Uvnitř tedy Windows obsluhují a uchovávají text jako UTF-16.

Každý znak v tomto kódování je v optimálním případě uložen na dvou bajtech. Avšak dva bajty by neumožnily uložit všechny znaky unicode. Proto se pro některé znaky používá dvojice dvou bajtových hodnot.

Pokud píšete své aplikace pro KDE nebo prostě jen používáte knihovnu Qt, budou všechny vaše texty předané knihovně Qt uloženy jako UTF-16, neboť vývojáři Qt zvolili UTF-16 jako svůj standardní formát ukládání textových řetězců.

UTF-32

UTF-32 se používá na Linuxu pro std::wstring. Typ wchar_t je zde 32-bitový, na rozdíl od Windows (Microsoft Visual C++), kde wchar_t má 16 bitů a std::wstring tedy bude také pouze UTF-16.

Bohužel, ani u UTF-32 se nemůžeme spolehnout, že každých 32-bitů bude reprezentovat jeden znak na obrazovce. Unicode totiž umožňuje například umístit diakritiku nad libovolný znak. Jedním z využití je lepší vyjádření výslovnosti daného jazyka pro cizince. Jako drobný příklad nám může sloužit anglická výslovnost, která používá kombinace znaků, které nejsou standardní součástí ASCII znakové sady a vypomáhá si kdejakým znaménkem, aby lépe vyjádřila přízvuk.

Paměťové nároky

Není pravdou, že UTF-8 je nejúspornější variantou pro ukládání textu. Nejúspornější může být pro anglický text, kde může využívat téměř výhradně jednobajtových znaků. Pravděpodobně bude nejúspornější i po dobu, kdy budete překládat vaši aplikaci do jazyků založených na latince, t.j. znacích abecedy většiny zemí Evropy, Ameriky a dalších kontinentů. Nicméně už při pokusu překladu do ruštiny (používá znaky cyrilice) mám velké pochybnosti o úspornosti UTF-8, nemluvě už vůbec o japonštině, čínštině a jazycích arabského světa. Všechny tyto jazyky budou pro téměř všechny znaky využívat dvou až šestibajtové reprezentace znaků. Pro tyto jazyky je paměťově výhodnější používat UTF-16.

Rychlost

Pokud neděláme aplikaci zpracovávající velké množství textu, není otázka rychlosti zpracování textů většinou problém. Většina funkcí pro práci s unicode je efektivně napsaná a není potřeba zvažovat zvláštní optimalizace. Obecné doporučení při designu aplikace je použít kódování, které je používáno na dané platformě. Píšete-li čistě pro Linux, není špatnou volbou UTF-8. Používáte-li knihovnu Qt nebo píšete-li čistě pro Windows, UTF-16 zabrání zbytečným konverzím mezi kódováním textu vaší aplikace a Qt nebo Windows. Chcete-li zůstat platformě neutrální a nepoužívat Qt, zůstane pravděpodobně vaše volba mezi std::string (UTF-8) a std::wstring (UTF-16 na Windows, UTF-32 na Linuxu).

Příklady

Používá-li váš zdroják unicode, je velmi snadné tisknout text do konzole. Tedy, alespoň na mém Linuxu Ubuntu 10.10. Ale čekám, že příklad bude fungovat na všech moderních linuxových distribucích. Windowsáci moment počkají na příklad funkční na jejich OS a linuxoví odborníci můžou žhavit závity, aby přidali kus moudrosti do diskuze pod článkem.

#include <stdio.h>
int main(int argc, char* argv[])
{
   printf("%s\n", "Pozdrav z Evropy: Schöne Grüße");
   printf("%s\n", "Něco z Arabštiny: يؤلمني.");
   printf("%s\n", "A ještě něco z Japonštiny: 私はガラスを食");
   char* twochars = "\xe6\x97\xa5\xd1\x88";
   printf("%s\n", twochars);
   return 0;
}

Chceme-li použít std::string situace je jednoduchá. Pro std::wstring však nefunguje L"Текст на кирилица". Pokud někdo zná důvod, ať jej uvede v diskuzi pod článkem.

#include <iostream>
std::wstring stringToWString(const std::string& s)
{
   std::wstring temp(s.length(),L' ');
   std::copy(s.begin(), s.end(), temp.begin());
   return temp;
}
int main(int argc, char* argv[])
{
   std::string s("Pozdrav z Evropy: Schöne Grüße");
   std::cout << s << std::endl;
   std::wstring ws(stringToWString("Текст на кирилица"));
   std::wcout << ws << std::endl;
   return 0;
}

A nyní již k netrpělivým Windowsákům. Naneštěstí, programování na Windows je v mnoha případech nestandardní a občas se musíte uchýlit k metodě pokus-omyl, abyste vyvinuli něco, co je ve skutečnosti tak jednoduché jako výpis textu do konzole.

#include <windows.h>
#include <iostream>
#include <assert.h>
// multi-byte string to wide-character string
static std::wstring mbToWString(const char *s)
{
   size_t l = strlen(s)+1; // include \n in the string length
   wchar_t *str = new wchar_t[l * sizeof(wchar_t)];
   int r = MultiByteToWideChar(CP_UTF8, 0, s, int(l), str, int(l));
   assert(r > 0 && "MultiByteToWideChar() failed.");
   return str;
}
int main(int argc, char* argv[])
{
   DWORD tmp;
   std::wstring ws1(mbToWString("Pozdrav z Evropy: Schöne Grüße\n"));
   WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws1.c_str(), (DWORD)ws1.length(), &tmp, NULL);
   std::wstring ws2(mbToWString("Něco z Arabštiny: يؤلمني.\n"));
   WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws2.c_str(), (DWORD)ws2.length(), &tmp, NULL);
   std::wstring ws3(mbToWString("A ještě něco z Japonštiny: 私はガラスを食\n"));
   WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws3.c_str(), (DWORD)ws3.length(), &tmp, NULL);
   std::wstring ws4(mbToWString("Текст на кирилица\n"));
   WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws4.c_str(), (DWORD)ws4.length(), &tmp, NULL);
   MessageBoxW(NULL, mbToWString("Pozdrav z Evropy: Schöne Grüße\n"
                                 "Něco z Arabštiny: يؤلمني.\n"
                                 "A ještě něco z Japonštiny: 私はガラスを食\n"
                                 "Текст на кирилица").c_str(), L"UTF-8", 0);
   return 0;
}

V message boxu uvidíte krásný unicode string, zahrnující i Arabštinu. Avšak v konzoli jsou stále ještě trable. České a německé znaky se sice zobrazily správně, ale japonština či ruština ne. Domnívám se, že je to problém fontu v konzoli a neobjevil jsem způsob, jak jej změnit. Při pokusech s printf a wprintf byl výsledek ještě horší. Ve všech případech bylo nutné použít funkci mbToWString, která zkonvertuje UTF-8 string ve zdrojáku do UTF-16, který je nativní na Windows. Pak již windowsovské funkce s W na konci začínají konečně dělat to, co od nich programátor očekává. Má-li někdo více zkušeností, ať se podělí pod článkem.

Qt nám nabízí množství funkcí pro práci s texty a jejich kódováním. Omezím se jen na pokročilejší věci a budu předpokládat znalost základní práce s textem v Qt a třídou QString. Při importu textu třídou QString často dochází ke konverzi kódování textu z externího na vnitřní reprezentaci. Vnitřní reprezentace je Utf-16, přičemž externí může být různá. Máme různé importní funkce, např. QString::fromAs­cii(), nebo QString::from­Latin1(), či QString::from­Local8Bit(). Tyto funkce používají vnitřně různé Qt kodeky, které provádějí vlastní konverzi. Teoreticky můžeme tyto kodeky i nastavovat dle vlastní potřeby. A opravdu toho bude potřeba. Nicméně nepředbíhejme a podívejme se na různé možnosti konstrukce QString a jeho obsahu. Samozřejmě nevyčerpáme všechny možnosti, ale alespoň ty často používané:

// konstrukce využívající kodek pro CStrings
QString a("Pozdrav z Evropy: Schöne Grüße\n");
QString b = QString::fromStdString("Něco z Arabštiny: يؤلمني.\n");
// konstrukce využívající kodek pro locale – lokálně nastavenou znakovou sadu
QString c = QString::fromAscii("Pozdrav z Evropy: Schöne Grüße\n");
QString d = QString::fromLocal8Bit("Něco z Arabštiny: يؤلمني.\n");
// konstrukce provádějící konverzi z Latin1 znakové sady
QString e = QLatin1String("Pozdrav z Evropy: Schöne Grüße\n");
QString f = QString::fromLatin1("Něco z Arabštiny: يؤلمني.\n");
// konstrukce provádějící konverzi z Utf-8
QString g = QString::fromUtf8("Utf-8 string: Schöne Grüße\n");
// výpis do Linuxové konzole
QString s(a+b+c+d+e+f+g);
std::cout << s;
std::cout << std::endl;
// výpis do Windowsové konzole
#if defined(__WIN32__) || defined(_WIN32)
DWORD tmp;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), s.utf16(), (DWORD)s.length(), &tmp, NULL);
#endif
// text v okně
QMessageBox::information(NULL, QString("Info"), s);

Spustíte-li tento zdroják na Linuxu, měl by fungovat bez nejmenšího zaváhání. Jediný text, který bude vždy zobrazen špatně, je ten, který jsme vytvořili pomocí Latin1 konverze – předhodíme-li jí arabské znaky, nemůže konverze dopadnout dobře. Trochu překvapivě fungují QString::fromAscii a fromLocal8Bit správně, neboť použijou korektní konverzi z Utf-8 pravděpodobně díky tomu, že můj Ubuntu 10.10 používá Utf-8 locale. A konverzní rutiny z c-stringů jsou nastaveny také korektně na této platformě, aby používaly Utf-8.

Jiná situace je na Windows – z pohledu našich příkladů poněkud komplikované platformě. Na první pokus se vypíše korektně pouze text zkonstruovaný přes QString::fromUt­f8(). Ostatní texty jsou v pořádku, dokud používají pouze standardní znaky bez diakritiky a arabštiny. Kodeky pro c-strings a locale jako by nebyly nastaveny správně. Zdá se, jako by Qt (testováno na verzi 4.7.1 v MSVC 2005) nevědělo, že náš zdroják je napsán v Utf-8 a že chceme, aby použil Utf-8 kodeky. Můžeme mu je tedy nastavit manuálně. Nad náš kód přidáme:

davame_internetu_obsah
       
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

Po té již vidíme vidíme většinu textu korektně vypsaného do konzole i do message boxu. Špatně zůstává pouze text zkonvertovaný Latin1 kodekem – přesně jak čekáme. Navíc MSVC zřejmě neumí vypisovat unicode text přes std::cout << s. Naštěstí jde situace obejít přes funkci WriteConsoleW().

Závěrem hodně štěstí s Unicode ve vašich programech a neváhejte se podělit se svými zkušenostmi v diskuzi pod článkem, případně přidat další zajímavé informace užitečné nám všem.

Školení: Linux – Firewall, Samba, VPN

Na třídenním školení se naučíte nainstalovat a spravovat Firewall a Router, SAMBA Doménový a Souborový server. Dále si zprovozníte vlastní, zabezpečený VPN server.

Podrobnější informace a přihláška

Ohodnoťte jako ve škole:
Průměrná známka 2,89

Přehled názorů

Příklady ve článku
P_V 27. 12. 2010 00:08
Nový
└ 
Re: Příklady ve článku
Biktop 28. 12. 2010 01:18
Nový
Programovani ve windows je nestandardni
Mat 27. 12. 2010 00:50
Nový
└ 
Re: Programovani ve windows je nestandardni
ajtak :P 27. 12. 2010 03:04
Nový
 
└ 
Re: Programovani ve windows je nestandardni
PC John 29. 12. 2010 22:32
Nový
Chybička u popisu UTF-8
Jakub Galgonek 27. 12. 2010 03:05
Nový
└ 
Re: Chybička u popisu UTF-8
Radovan 27. 12. 2010 09:01
Nový
 
├ 
Re: Chybička u popisu UTF-8
Jakub Galgonek 27. 12. 2010 09:10
Nový
 
│
└ 
Re: Chybička u popisu UTF-8
Radovan 28. 12. 2010 11:09
Nový
 
│
 
└ 
Re: Chybička u popisu UTF-8
Jakub Galgonek 28. 12. 2010 11:43
Nový
 
│
 
 
└ 
Re: Chybička u popisu UTF-8
Radovan 29. 12. 2010 15:37
Nový
 
│
 
 
 
├ 
Re: Chybička u popisu UTF-8
Jakub Galgonek 29. 12. 2010 16:02
Nový
 
│
 
 
 
│
└ 
Re: Chybička u popisu UTF-8
Sten 3. 1. 2011 11:24
Nový
 
│
 
 
 
│
 
└ 
Re: Chybička u popisu UTF-8
Jakub Galgonek 4. 1. 2011 09:58
Nový
 
│
 
 
 
│
 
 
└ 
Re: Chybička u popisu UTF-8
Radovan 5. 1. 2011 16:09
Nový
 
│
 
 
 
│
 
 
 
└ 
Re: Chybička u popisu UTF-8
Jakub Galgonek 5. 1. 2011 18:01
Nový
 
│
 
 
 
└ 
Re: Chybička u popisu UTF-8
Jakub Galgonek 29. 12. 2010 16:57
Nový
 
└ 
Re: Chybička u popisu UTF-8
CHe 27. 12. 2010 09:33
Nový
článek k ničemu
xurfa 27. 12. 2010 08:21
Nový
├ 
Re: článek k ničemu
X 27. 12. 2010 10:35
Nový
└ 
Re: článek k ničemu
lxd 27. 12. 2010 22:55
Nový
Co je na tom komplikovaného?
Johnnie 27. 12. 2010 09:37
Nový
└ 
Re: Co je na tom komplikovaného?
PC John 27. 12. 2010 10:35
Nový
 
├ 
Re: Co je na tom komplikovaného?
pjoter 27. 12. 2010 15:41
Nový
 
│
└ 
Re: Co je na tom komplikovaného?
PC John 27. 12. 2010 16:59
Nový
 
│
 
└ 
Re: Co je na tom komplikovaného?
pjoter 27. 12. 2010 17:10
Nový
 
│
 
 
└ 
Re: Co je na tom komplikovaného?
PC John 27. 12. 2010 18:40
Nový
 
└ 
Re: Co je na tom komplikovaného?
Johnnie 28. 12. 2010 16:36
Nový
 
 
└ 
Re: Co je na tom komplikovaného?
PC John 29. 12. 2010 21:36
Nový
chyby
Jardík 27. 12. 2010 11:45
Nový
Paměťové nároky
Jakub Galgonek 27. 12. 2010 11:57
Nový
Ta japonská věta je špatně
apoc9 27. 12. 2010 16:57
Nový
└ 
Re: Ta japonská věta je špatně
Zdenek - 27. 12. 2010 19:19
Nový
 
└ 
Re: Ta japonská věta je špatně
fri 27. 12. 2010 20:53
Nový
L"' wstring
Sten 27. 12. 2010 17:15
Nový
Re: Napsat správně mezinárodní aplikaci v Qt nemusí být snadné
Biktop 28. 12. 2010 01:06
Nový
└ 
Re: Napsat správně mezinárodní aplikaci v Qt nemusí být snadné
PC John 29. 12. 2010 21:48
Nový
Nesmyslný článek
Petr Bravenec 28. 12. 2010 10:46
Nový
└ 
Re: Nesmyslný článek
Karel2 29. 12. 2010 09:46
Nový
 
└ 
Re: Nesmyslný článek
PC John 29. 12. 2010 22:09
Nový
 
 
└ 
Re: Nesmyslný článek
Petr Bravenec 30. 12. 2010 10:36
Nový
 
 
 
└ 
Re: Nesmyslný článek
Petr Bravenec 30. 12. 2010 10:38
Nový
Cyrilice
Jirka 28. 12. 2010 10:52
Nový
Programování ve Windows
Jirka 28. 12. 2010 10:55
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem