Widziałem wiele osób w społeczności C ++ (szczególnie ## c ++ na freenode) niechętnie używają wstrings
i wchar_t
oraz ich używania w api systemu Windows. Co jest dokładnie „nie tak” w przypadku wchar_t
i wstring
i jeśli chcę wspierać internacjonalizację, jakie są alternatywy dla szerokich znaków?
86
Odpowiedzi:
Co to jest wchar_t?
wchar_t jest zdefiniowany w taki sposób, że kodowanie znaków dowolnego ustawienia regionalnego można przekonwertować na reprezentację wchar_t, gdzie każdy wchar_t reprezentuje dokładnie jeden punkt kodowy:
To nie wymagają wchar_t być wystarczająco duże, aby reprezentować dowolny znak ze wszystkich lokalizacjach jednocześnie. Oznacza to, że kodowanie używane dla wchar_t może się różnić w zależności od lokalizacji. Oznacza to, że niekoniecznie można przekonwertować ciąg na wchar_t przy użyciu jednego ustawienia narodowego, a następnie przekonwertować go z powrotem na znak przy użyciu innego ustawienia narodowego. 1
Ponieważ użycie wchar_t jako wspólnej reprezentacji między wszystkimi lokalizacjami wydaje się być podstawowym zastosowaniem wchar_t w praktyce, możesz się zastanawiać, do czego jest dobry, jeśli nie do tego.
Pierwotnym zamiarem i celem wchar_t było uproszczenie przetwarzania tekstu poprzez zdefiniowanie go tak, że wymaga odwzorowania jeden do jednego z jednostek kodu ciągu na znaki tekstu, umożliwiając w ten sposób użycie tych samych prostych algorytmów, które są używane z ciągami ascii do pracy z innymi językami.
Niestety, sformułowanie specyfikacji wchar_t zakłada odwzorowanie jeden do jednego między znakami i punktami kodowymi, aby to osiągnąć. Unicode łamie to założenie 2 , więc nie można bezpiecznie używać wchar_t również dla prostych algorytmów tekstowych.
Oznacza to, że oprogramowanie przenośne nie może używać wchar_t ani jako wspólnej reprezentacji tekstu między lokalizacjami, ani w celu umożliwienia użycia prostych algorytmów tekstowych.
Jakie zastosowanie ma dzisiaj wchar_t?
W każdym razie niewiele, jak na przenośny kod. Jeśli
__STDC_ISO_10646__
jest zdefiniowane, to wartości wchar_t bezpośrednio reprezentują punkty kodowe Unicode z tymi samymi wartościami we wszystkich lokalizacjach. To sprawia, że konwersje międzylokalne, o których mowa wcześniej, są bezpieczne. Jednak nie możesz polegać tylko na nim, aby zdecydować, że możesz użyć wchar_t w ten sposób, ponieważ podczas gdy większość platform uniksowych definiuje to, Windows nie używa tego samego ustawienia narodowego wchar_t we wszystkich lokalizacjach.Powodem, dla którego system Windows nie definiuje,
__STDC_ISO_10646__
jest to, że system Windows używa kodowania UTF-16 jako swojego kodowania wchar_t, a ponieważ UTF-16 używa par zastępczych do reprezentowania punktów kodowych większych niż U + FFFF, co oznacza, że UTF-16 nie spełnia wymagań dla__STDC_ISO_10646__
.W przypadku kodu specyficznego dla platformy wchar_t może być bardziej przydatny. Zasadniczo jest to wymagane w systemie Windows (np. Niektóre pliki po prostu nie mogą być otwierane bez użycia nazw plików wchar_t), chociaż Windows jest jedyną platformą, na której jest to prawdą, o ile wiem (więc może możemy myśleć o wchar_t jako o „Windows_char_t”).
Z perspektywy czasu wchar_t najwyraźniej nie jest przydatny do upraszczania obsługi tekstu lub do przechowywania tekstu niezależnego od ustawień regionalnych. Kod przenośny nie powinien próbować używać go do tych celów. Kod nieprzenośny może okazać się przydatny po prostu dlatego, że wymaga tego niektóre API.
Alternatywy
Alternatywą, którą lubię, jest użycie ciągów C zakodowanych w UTF-8, nawet na platformach niezbyt przyjaznych dla UTF-8.
W ten sposób można napisać przenośny kod przy użyciu wspólnej reprezentacji tekstowej na różnych platformach, użyć standardowych typów danych zgodnie z ich przeznaczeniem, uzyskać obsługę języka dla tych typów (np. obsługa standardowej biblioteki, obsługa debuggera (może być potrzebnych więcej trików), itp. W przypadku szerokich znaków uzyskanie tego wszystkiego jest zazwyczaj trudniejsze lub niemożliwe i możesz otrzymać różne elementy na różnych platformach.
Jedną rzeczą, której UTF-8 nie zapewnia, jest możliwość korzystania z prostych algorytmów tekstowych, jakie są możliwe w ASCII. W tym UTF-8 nie jest gorszy niż jakiekolwiek inne kodowanie Unicode. W rzeczywistości można to uznać za lepsze, ponieważ reprezentacje jednostek wielokodowych w UTF-8 są bardziej powszechne, a więc błędy w kodzie obsługującym takie reprezentacje o zmiennej szerokości znaków są bardziej prawdopodobne, że zostaną zauważone i naprawione, niż gdybyś próbował trzymać się UTF -32 z NFC lub NFKC.
Wiele platform używa UTF-8 jako swojego natywnego kodowania znaków, a wiele programów nie wymaga żadnego znaczącego przetwarzania tekstu, więc pisanie umiędzynarodowionego programu na tych platformach niewiele różni się od pisania kodu bez uwzględnienia internacjonalizacji. Pisanie szerzej przenośnego kodu lub pisanie na innych platformach wymaga wstawiania konwersji na granicach interfejsów API korzystających z innych kodowań.
Inną alternatywą używaną przez niektóre programy jest wybranie reprezentacji międzyplatformowej, takiej jak krótkie tablice bez znaku przechowujące dane UTF-16, a następnie dostarczenie całej obsługi bibliotek i po prostu życie z kosztami obsługi języka itp.
C ++ 11 dodaje nowe rodzaje szerokich znaków jako alternatywę dla wchar_t, char16_t i char32_t z towarzyszącymi funkcjami języka / biblioteki. W rzeczywistości nie ma gwarancji, że będą to UTF-16 i UTF-32, ale nie wyobrażam sobie, aby jakakolwiek większa implementacja używała niczego innego. C ++ 11 poprawia również obsługę UTF-8, na przykład z literałami łańcuchowymi UTF-8, więc nie będzie konieczne oszukiwanie VC ++ do tworzenia zakodowanych ciągów UTF-8 (chociaż mogę nadal to robić, zamiast używać
u8
prefiksu) .Alternatywy, których należy unikać
TCHAR: TCHAR służy do migracji starych programów Windows, które zakładają starsze kodowanie z char do wchar_t i najlepiej o nim zapomnieć, chyba że twój program został napisany w jakimś poprzednim tysiącleciu. Nie jest przenośny i jest z natury niespecyficzny co do jego kodowania, a nawet typu danych, co czyni go bezużytecznym z żadnym interfejsem API innym niż TCHAR. Ponieważ jego celem jest migracja do wchar_t, co widzieliśmy powyżej, nie jest dobrym pomysłem, używanie TCHAR nie ma żadnej wartości.
1. Znaki, które są reprezentowane w łańcuchach wchar_t, ale które nie są obsługiwane w żadnym ustawieniu narodowym, nie muszą być reprezentowane przez pojedynczą wartość wchar_t. Oznacza to, że wchar_t może używać kodowania o zmiennej szerokości dla niektórych znaków, co jest kolejnym wyraźnym naruszeniem intencji wchar_t. Chociaż można spierać się, że znak, który jest reprezentowany przez wchar_t, wystarczy, aby powiedzieć, że ustawienia narodowe „obsługują” ten znak, w którym to przypadku kodowanie o zmiennej szerokości nie jest dozwolone, a użycie UTF-16 w systemie Windows jest niezgodne.
2. Unicode umożliwia przedstawienie wielu znaków w wielu punktach kodowych, co stwarza te same problemy w przypadku prostych algorytmów tekstowych, co w przypadku kodowania o zmiennej szerokości. Nawet jeśli ściśle przestrzega się złożonej normalizacji, niektóre znaki nadal wymagają wielu punktów kodowych. Zobacz: http://www.unicode.org/standard/where/
źródło
fopen
plik, którego nazwa zawiera znaki inne niż ANSI.Nie ma nic "złego" w wchar_t. Problem polega na tym, że w NT 3.x dni Microsoft zdecydował, że Unicode jest dobry (jest) i zaimplementował Unicode jako 16-bitowe znaki wchar_t. Tak więc większość literatury firmy Microsoft z połowy lat 90-tych prawie zrównuje Unicode == utf16 == wchar_t.
Co niestety wcale nie jest prawdą. „Szerokie znaki” niekoniecznie muszą mieć 2 bajty, na wszystkich platformach, w każdych okolicznościach.
To jeden z najlepszych podkładów na temat „Unicode” (niezależny od tego pytania, niezależny od C ++), jaki kiedykolwiek widziałem: bardzo go polecam:
I szczerze wierzę, że najlepszym sposobem radzenia sobie z „8-bitowym ASCII” w porównaniu z „szerokimi znakami Win32” w porównaniu z „wchar_t-in-general” jest po prostu zaakceptowanie, że „Windows jest inny”… i odpowiednio zakodować.
MOIM ZDANIEM...
PS:
Całkowicie zgadzam się z powyższym jamesdlinem:
źródło