Unicode to ciekawy wynalazek. Zamiast stosować wymyślne sposoby na “przełączanie” sposobu interpretowania zwykłych 8-bitowych znaków, ktoś mądry wymyślił po prostu, że obecnie nie ma większych przeciwwskazań, aby zwykły tekst zajmował dwa razy więcej miejsca niż dotychczas. Powstał więc standard UTF-16, w którym stron kodowych nie ma, a każdy znak jest zapisany za pomocą jednego z 65536 kodów.
Ale jak to zwykle bywa z rozwiązaniami, które mają rozwiązywać istniejące problemy, Unicode natychmiast stworzył swoje własne :) Oprócz tego, że założone 16 bitów szybko okazało się za małe (stąd istnienie także UTF-32), powstał też szereg kłopotów praktycznych. Jednym z nich jest choćby to, że żyjemy w okresie przejściowym (który zresztą trwa już wybitnie długo) i że w użyciu jest zarówno unikod, jak i zwykłe ANSI. A na pierwszy rzut oka (ludzkiego i programowego) tekst jest po prostu ciągiem bajtów i bez zewnętrznych wskazówek nie jest możliwe określenie w stu procentach, czy został on zapisany w ANSI, UTF-8, UTF-16 czy UTF-32. Co więcej, znaki Unicode są oczywiście liczbami, a ponieważ zasadniczo zajmują one więcej niż jeden bajt, pojawia się problem z ustaleniem właściwej kolejności tych bajtów (little-endian lub big-endian).
Oba te problemy ma rozwiązywać tzw. znacznik porządku bajtów (Byte Order Mark), ale oczywiście jak większość dobrych praktyk, także i jego umieszczanie na początku dokumentów nie jest zbyt popularne :)
Z programistycznego punktu widzenia sprawa nie wygląda aczkolwiek aż tak źle i w większości przypadków jesteśmy w jednej z dwóch sytuacji. Pierwsza z nich to “nieświadome” korzystanie z unikodu (czy może raczej “szerokich”, dwubajtowych znaków), bo został o niego oparty używany przez nas język oraz platforma; najlepszymi przykładami są tu .NET i C# oraz Java.
Druga sytuacja to możliwość wyboru, z jakiego systemu będziemy korzystali. Bardzo dobre jest to, że prawie zawsze możemy zdecydować się na… oba, czyli potencjalne kompilowanie dwóch wersji: ANSI i Unicode. Pod Windows na przykład w programach pisanych w C++ wystarczy przestrzegać kilku prostych i dobrze znanych zasad:
char
(lub wchar_t
) należy – wszędzie tam, gdzie chodzi nam o znaki – używać typu TCHAR
, który w zależności od wersji zostanie zamieniony na jeden z tych dwóch.TEXT
(czyli TEXT("Coś")
zamiast po prostu "Coś"
), dzięki czemu zostaną one skompilowane jako łańcuchy ANSI lub Unicode.Niestety, słowo ‘niektóre’ sytuuje się dość daleko od ‘wszystkie’ i dlatego ostatecznie nie jest tak różowo. Czołowe miejsce na liście “niewspółpracujących” zajmuje standardowa biblioteka C++. Z nią trzeba sobie poradzić we własnym zakresie.
Nie jest to aczkolwiek bardzo trudne, jako że każdy kompilator definiuje makro dla rozróżnienia wersji ANSI i Unicode. Visual C++ na przykład włącza w tym przypadku symbol _UNICODE
, którego możemy użyć do stworzenia odpowiednich aliasów:
Możemy je umieścić we własnym pliku nagłówkowym i dołączać we własnych projektach. Ponieważ jednak typów zależnych od znaków jest w STL całkiem sporo, można z powodzeniem użyć chociażby rozwiązania zamieszczonego na CodeProject, w razie potrzeby rozszerzając je o kolejne aliasy.