Czy TCHAR jest nadal aktualny?

87

Jestem nowy w programowaniu Windows i po przeczytaniu książki Petzolda zastanawiam się:

czy nadal dobrą praktyką jest używanie TCHARtypu i _T()funkcji do deklarowania ciągów, czy też powinienem używać po prostu ciągów wchar_ti L""w nowym kodzie?

Skoncentruję się tylko na systemie Windows 2000 i nowszych, a mój kod od samego początku będzie i18n .

Fábio
źródło

Odpowiedzi:

15

Nadal używałbym składni TCHAR, gdybym wykonywał dziś nowy projekt. Nie ma dużej praktycznej różnicy między używaniem go a składnią WCHAR, a ja wolę kod, który jest wyraźny w typie znaku. Ponieważ większość funkcji API i obiektów pomocniczych przyjmuje / używa typów TCHAR (np. CString), po prostu warto z nich korzystać. Ponadto zapewnia elastyczność, jeśli w pewnym momencie zdecydujesz się użyć kodu w aplikacji ASCII lub jeśli Windows kiedykolwiek ewoluuje do Unicode32 itp.

Jeśli zdecydujesz się wybrać trasę WCHAR, będę o tym wyraźnie mówić. Oznacza to, że używaj CStringW zamiast CString i rzutuj makra podczas konwersji do TCHAR (np .: CW2CT).

W każdym razie to moja opinia.

Nacięcie
źródło
Rzeczywiście, to nadal będzie działać, gdy kodowanie znaków zostanie ostatecznie zmienione „ponownie”.
Medinoc,
11
Wolisz kod, który wyraźnie określa, jaki jest typ znaku, i dlatego używasz typu, który czasami jest taki, a czasami inny? Bardzo przekonujące.
Deduplicator
4
−1 za niespójność odnotowaną przez @Deduplicator oraz za poradę dotyczącą ujemnej wypłaty, aby użyć makra, które może być dowolne (i na ogół nie będzie testowane pod kątem więcej niż jednej określonej wartości).
Pozdrawiam i hth. - Alf,
90

Krótka odpowiedź: NIE .

Jak wszyscy inni już napisali, wielu programistów nadal używa TCHAR i odpowiadających im funkcji. Moim skromnym zdaniem cała koncepcja była złym pomysłem . Przetwarzanie napisów UTF-16 znacznie różni się od prostego przetwarzania łańcuchów ASCII / MBCS. Jeśli używasz tych samych algorytmów / funkcji z obiema z nich (na tym opiera się idea TCHAR!), Uzyskasz bardzo złą wydajność w wersji UTF-16, jeśli robisz coś więcej niż zwykłą konkatenację ciągów (np. parsowanie itp.). Głównym powodem są Surogaty .

Z jedynym wyjątkiem, kiedy naprawdę musisz skompilować swoją aplikację dla systemu, który nie obsługuje Unicode, nie widzę powodu, aby wykorzystywać ten bagaż z przeszłości w nowej aplikacji.

Sascha
źródło
6
Ciekawostka: UTF-16 nie zawsze istniał na platformie NT. Zastępcze punkty kodowe zostały wprowadzone w standardzie Unicode 2.0 w 1996 roku, czyli w tym samym roku, w którym wydano NT 4. Aż do IIRC (w tym) Windows 2000 wszystkie wersje NT używały UCS-2, efektywnie podzbioru UTF-16, który zakładał, że każdy znak jest reprezentowalny z jednym punktem kodowym (tj. Bez surogatów).
0xC0000022L,
3
przy okazji, chociaż zgadzam się, że TCHARnie powinno być już używane, nie zgadzam się, że to był zły pomysł. Uważam również, że jeśli zdecydujesz się być wyraźny zamiast używać TCHAR, powinieneś być wyraźny wszędzie . To znaczy nie używaj funkcji z TCHAR/ _TCHAR(takimi jak _tmain) w ich deklaracji. Krótko mówiąc: bądź konsekwentny. +1, nadal.
0xC0000022L,
3
To był dobry pomysł, kiedy został wprowadzony, ale powinien być nieistotny w nowym kodzie.
Adrian McCarthy
4
Podajesz nieprawdziwe informacje, do czego TCHARpoczątkowo wprowadzono: Aby ułatwić tworzenie kodu dla wersji Windows opartych na Windows 9x i Windows NT. W tamtym czasie implementacją UTF-16 w Windows NT była UCS-2, a algorytmy analizy / manipulacji na ciągach znaków były identyczne. Nie było żadnych surogatów. Nawet w przypadku surogatów algorytmy DBCS (jedyne obsługiwane kodowanie MBCS dla systemu Windows) i UTF-16 są takie same: w każdym kodowaniu punkt kodowy składa się z jednej lub dwóch jednostek kodu.
Niespodziewane
Załóżmy, że chcę użyć FormatMessage () do konwersji wartości z WSAGetLastError () na coś, co można wydrukować. Dokumentacja WSAGetLastError () mówi, że pobiera LPTSTR jako wskaźnik do bufora. Naprawdę nie mam wielkiego wyboru, ale muszę używać TCHAR, nie?
Edward Falk
80

Muszę się zgodzić z Saschą. Podstawowym założeniem TCHAR/ _T()/ etc. jest to, że możesz napisać aplikację opartą na "ANSI", a następnie w magiczny sposób nadać jej obsługę Unicode poprzez zdefiniowanie makra. Ale opiera się to na kilku złych założeniach:

Aktywnie budujesz oprogramowanie w wersji MBCS i Unicode

W przeciwnym razie będą ślizgać się i używać zwykłych char*ciągów w wielu miejscach.

Że w literałach _T ("...") nie używasz znaków z ukośnikiem odwrotnym spoza ASCII

O ile Twoim kodowaniem „ANSI” nie jest ISO-8859-1, wynikowe char*i wchar_t*literały nie będą reprezentować tych samych znaków.

Te ciągi UTF-16 są używane tak jak ciągi „ANSI”

Oni nie są. Unicode wprowadza kilka koncepcji, które nie istnieją w większości starszych kodowań znaków. Surogaty. Łączenie znaków. Normalizacja. Reguły wielkości liter warunkowych i zależnych od języka.

I być może najważniejsze, fakt, że UTF-16 rzadko jest zapisywany na dysku lub przesyłany przez Internet: UTF-8 jest preferowany do reprezentacji zewnętrznej.

Twoja aplikacja nie korzysta z Internetu

(To może być prawidłowe założenie dla twojego oprogramowania, ale ...)

Sieć działa na UTF-8 i wielu rzadszych kodowaniach . TCHARKoncepcja uznaje tylko dwa: "ANSI" (które nie mogą być UTF-8 ) i "Unicode" (UTF-16). Może to być przydatne do wykonywania wywołań interfejsu API systemu Windows z obsługą Unicode, ale jest cholernie bezużyteczne, aby aplikacje internetowe i e-mailowe obsługiwały Unicode.

Że nie używasz bibliotek innych niż Microsoft

Nikt inny nie używa TCHAR. Poco używa std::stringi UTF-8. SQLite ma wersje UTF-8 i UTF-16 swojego API, ale nie TCHAR. TCHARnie ma go nawet w standardowej bibliotece, więc nie, std::tcoutchyba że chcesz to zdefiniować samodzielnie.

Co polecam zamiast TCHAR

Zapomnij, że istnieją kodowania „ANSI”, z wyjątkiem sytuacji, gdy musisz odczytać plik, który nie jest prawidłowym UTF-8. Zapomnij o tym TCHAR. Zawsze wywołuj wersję „W” funkcji Windows API. #define _UNICODEtylko po to, aby upewnić się, że przypadkowo nie wywołasz funkcji „A”.

Zawsze używaj kodowania UTF dla łańcuchów: UTF-8 dla charłańcuchów i UTF-16 (w systemie Windows) lub UTF-32 (w systemach typu Unix) dla wchar_tłańcuchów. typedef UTF16i UTF32typy postaci, aby uniknąć różnic między platformami.

dan04
źródło
6
Wezwanie w 2012 r .: nadal istnieją aplikacje, które trzeba utrzymywać bez nich #define _UNICODE. Koniec transmisji :)
0xC0000022L
12
@ 0xC0000022L pytanie dotyczyło nowego kodu. Kiedy utrzymujesz stary kod, musisz oczywiście pracować w środowisku, dla którego kod jest napisany. Jeśli utrzymujesz aplikację w języku COBOL, nie ma znaczenia, czy COBOL jest dobrym językiem, czy nie, utknąłeś z tym. A jeśli utrzymujesz aplikację, która opiera się na TCHAR, nie ma znaczenia, czy była to dobra decyzja, czy nie, utknąłeś z nią.
jalf
2
Rzeczywiście, TCHAR nie jest przydatny, chyba że w COBOL)
Pavel Radzivilovsky
1
_UNICODEkontroluje, w jaki sposób mapowania tekstu ogólnego są rozwiązywane w CRT. Jeśli nie chcesz wywoływać wersji ANSI interfejsu API systemu Windows, musisz zdefiniować UNICODE.
Niespodziewane
18

Jeśli zastanawiasz się, czy nadal jest w praktyce, to tak - nadal jest dość często używany. Nikt nie będzie wyglądał śmiesznie na Twój kod, jeśli używa on TCHAR i _T („”). Projekt, nad którym teraz pracuję, konwertuje z ANSI na Unicode - i wybieramy trasę przenośną (TCHAR).

Jednak...

Moim głosem byłoby zapomnieć o wszystkich przenośnych makrach ANSI / UNICODE (TCHAR, _T ("") i wszystkie wywołania _tXXXXXX, itp ...) i po prostu założyć unicode wszędzie. Naprawdę nie widzę sensu bycia przenośnym, jeśli nigdy nie będziesz potrzebować wersji ANSI. Użyłbym bezpośrednio wszystkich funkcji i typów szerokich znaków. Preprend wszystkie literały łańcuchowe za pomocą L.

Aardvark
źródło
3
Możesz napisać kod, którego będziesz chciał użyć gdzie indziej, gdzie potrzebujesz wersji ANSI, lub (jak powiedział Nick) Windows może przenieść się na DCHAR lub cokolwiek innego, więc nadal uważam, że bardzo dobrym pomysłem jest skorzystanie z TCHAR zamiast WCHAR.
arke
Wątpię, czy Windows kiedykolwiek przełączy się na UTF-32.
dan04
7
-1 dla rekomendacji UTF-16. Nie tylko tworzy to nieprzenośny (skoncentrowany na systemie Windows) kod, co jest niedopuszczalne dla bibliotek - nawet jeśli może być używane w najprostszych przypadkach, takich jak kod interfejsu użytkownika - nie jest wydajne nawet w samym systemie Windows. utf8everywhere.org
Pavel Radzivilovsky
11

Artykuł Wprowadzenie do programowania w systemie Windows w witrynie MSDN mówi

Nowe aplikacje powinny zawsze wywoływać wersje Unicode (API).

TEKST i TCHAR makra są mniej użyteczne dzisiaj, ponieważ wszystkie aplikacje powinny używać Unicode.

Trzymałbym się wchar_ti L"".

Steven
źródło
4
Steven, cytujesz tekst napisany przez kogoś, kto nie rozumie znaczenia słowa „Unicode”. To jeden z tych niefortunnych dokumentów z czasów zamieszania z UCS-2.
Pavel Radzivilovsky
2
@PavelRadzivilovsky: Dokument został napisany dla systemu, w którym Unicode i UTF-16LE są powszechnie używane zamiennie. Chociaż technicznie niedokładne, niemniej jednak jest jednoznaczne. Jest to również wyraźnie podkreślone we wstępie do tego samego tekstu: „Windows reprezentuje znaki Unicode przy użyciu kodowania UTF-16 […]” .
Niespodziewane
11

Chciałbym zasugerować inne podejście (żadne z dwóch).

Podsumowując, użyj char * i std :: string, zakładając kodowanie UTF-8 i wykonuj konwersje do UTF-16 tylko podczas zawijania funkcji API.

Więcej informacji i uzasadnienie tego podejścia w programach systemu Windows można znaleźć pod adresem http://www.utf8everywhere.org .

Pavel Radzivilovsky
źródło
@PavelRadzivilovsky, czy wdrażając twoją sugestię w aplikacji VC ++, ustawilibyśmy znak VC ++ ustawiony na „Brak” lub „Wielobajt (MBCS)”? Pytam o to, że właśnie zainstalowałem Boost :: Locale, a domyślnym zestawem znaków był MBCS. FWIW, moja czysta aplikacja ASCII była ustawiona na „Brak”, a teraz ustawiłem ją na „MBCS” (ponieważ będę używać w niej Boost :: Locale) i działa dobrze. Proszę doradź.
Caroline Beltran
Zgodnie z zaleceniami utf8everywhere ustawiłbym to na „Użyj zestawu znaków Unicode”. Zapewnia to dodatkowe bezpieczeństwo, ale nie jest wymagane. Autor Boost :: locale jest bardzo sprytnym facetem, jestem jednak pewien, że postąpił słusznie.
Pavel Radzivilovsky
1
UTF-8 Wszędzie mantra nie będzie dobrym rozwiązaniem, tylko dlatego, że powtarza się częściej. UTF-8 jest niewątpliwie atrakcyjnym kodowaniem do serializacji (np. Plików lub gniazd sieciowych), ale w systemie Windows często bardziej odpowiednie jest przechowywanie danych znakowych przy użyciu wewnętrznego kodowania UTF-16 i konwersja na granicach aplikacji. Jednym z powodów jest to, że UTF-16 jest jedynym kodowaniem, które można natychmiast przekonwertować na dowolne inne obsługiwane kodowanie. Tak nie jest w przypadku UTF-8.
Niespodziewane
„..UTF-16 to jedyne kodowanie, które można natychmiast przekonwertować na dowolne inne obsługiwane kodowanie”. co masz na myśli? Jaki jest problem z konwersją kodowania UTF-8 na cokolwiek innego?
Pavel Radzivilovsky
1
Nie rozumiem. Na cokolwiek innego - na przykład co? Np. UCS-4? Dlaczego nie? Wydaje się bardzo proste, cały algorytm numeryczny ...
Pavel Radzivilovsky,
7

TCHAR/ WCHARmoże wystarczyć w przypadku niektórych starszych projektów. Ale w przypadku nowych aplikacji powiedziałbym NIE .

Wszystkie te TCHAR/ WCHARrzeczy są tam ze względów historycznych. TCHARzapewnia pozornie schludny sposób (przebranie) przełączania między kodowaniem tekstu ANSI (MBCS) a kodowaniem tekstu Unicode (UTF-16). W przeszłości ludzie nie rozumieli liczby znaków we wszystkich językach świata. Założyli, że 2 bajty wystarczą do reprezentowania wszystkich znaków, a tym samym mają schemat kodowania znaków o stałej długości przy użyciu WCHAR. Jednak po wydaniu Unicode 2.0 w 1996 roku nie jest to już prawdą .

To znaczy: Bez względu na to, którego używasz w CHAR/ WCHAR/ TCHAR, część przetwarzania tekstu w twoim programie powinna być w stanie obsłużyć znaki o zmiennej długości do internacjonalizacji.

Więc faktycznie musisz zrobić więcej niż wybranie jednego z CHAR/ WCHAR/ TCHARdo programowania w Windows:

  1. Jeśli twoja aplikacja jest mała i nie wymaga przetwarzania tekstu (tj. Po prostu przekazywania ciągu tekstowego jako argumentów), trzymaj się WCHAR. Ponieważ w ten sposób łatwiej jest pracować z WinAPI z obsługą Unicode.
  2. W przeciwnym razie sugerowałbym użycie UTF-8 jako wewnętrznego kodowania i przechowywanie tekstów w łańcuchach znaków lub std :: string. I ukryj je do UTF-16 podczas wywoływania WinAPI. UTF-8 jest obecnie dominującym kodowaniem i istnieje wiele przydatnych bibliotek i narzędzi do przetwarzania łańcuchów UTF-8.

Sprawdź tę wspaniałą stronę internetową, aby uzyskać bardziej szczegółowe informacje: http://utf8everywhere.org/

Lampart
źródło
2
„UTF-8 jest teraz dominującym kodowaniem” - to okazało się błędne, pomijając drugą część cytatu ( „dla sieci World Wide Web” ). W przypadku aplikacji komputerowych najczęściej używanym natywnym kodowaniem znaków jest prawdopodobnie nadal UTF-16. Windows go używa, Mac OS X też, podobnie jak typy ciągów .NET i Java. To odpowiada za ogromną ilość kodu. Nie zrozum mnie źle, nie ma nic złego w UTF-8 do serializacji. Ale częściej niż nie (szczególnie w systemie Windows) okaże się, że wewnętrzne użycie UTF-16 jest bardziej odpowiednie.
Niespodziewane
4

Tak, absolutnie; przynajmniej dla makra _T. Nie jestem jednak taki pewien, jeśli chodzi o rzeczy o szerokim charakterze.

Powodem jest lepsza obsługa WinCE lub innych niestandardowych platform Windows. Jeśli masz 100% pewności, że twój kod pozostanie w systemie NT, prawdopodobnie możesz po prostu użyć zwykłych deklaracji łańcucha C. Jednak najlepiej jest skłaniać się ku bardziej elastycznemu podejściu, ponieważ znacznie łatwiej #definiować to makro na platformie innej niż Windows w porównaniu z przeglądaniem tysięcy wierszy kodu i dodawaniem go wszędzie na wypadek, gdyby trzeba było przenieść jakąś bibliotekę do Windows Mobile.

Nik Reiman
źródło
1
WinCE używa 16-bitowych ciągów wchar_t, podobnie jak Win32. Mamy dużą bazę kodu działającą na WinCE i Win32 i nigdy nie używamy TCHAR.
mhenry1384
2

IMHO, jeśli w twoim kodzie są TCHARs, pracujesz na złym poziomie abstrakcji.

Podczas przetwarzania tekstu używaj dowolnego typu ciągu, który jest dla Ciebie najwygodniejszy - mam nadzieję, że będzie to coś, co obsługuje Unicode, ale to zależy od Ciebie. W razie potrzeby wykonaj konwersję na granicach interfejsu OS API.

W przypadku ścieżek do plików utwórz własny typ niestandardowy zamiast używać ciągów. Pozwoli to na niezależne od systemu operacyjnego separatory ścieżek, zapewni łatwiejszy interfejs do kodowania niż ręczne łączenie i dzielenie ciągów i będzie o wiele łatwiejsze do dostosowania do różnych systemów operacyjnych (ansi, ucs-2, utf-8, cokolwiek) .

snemarch
źródło
Unicode ma co najmniej trzy obecne kodowania (UTF-8, UTF-16, UTF-32) i jedno przestarzałe kodowanie (UCS-2, podzbiór tego, co jest teraz UTF-16). Do którego się odnosisz? Pozostałe sugestie mi się podobają +1
0xC0000022L
2

Jedyne powody, dla których widzę, aby używać czegokolwiek innego niż jawne WCHAR, to przenośność i wydajność.

Jeśli chcesz, aby Twój ostateczny plik wykonywalny był jak najmniejszy, użyj funkcji char.

Jeśli nie zależy Ci na wykorzystaniu pamięci RAM i chcesz, aby internacjonalizacja była tak łatwa, jak proste tłumaczenie, użyj WCHAR.

Jeśli chcesz, aby Twój kod był elastyczny, użyj TCHAR.

Jeśli planujesz używać tylko znaków łacińskich, równie dobrze możesz użyć ciągów ASCII / MBCS, aby użytkownik nie potrzebował tak dużej ilości pamięci RAM.

Osoby, które są „i18n od samego początku”, mogą zaoszczędzić miejsce na kod źródłowy i po prostu korzystać ze wszystkich funkcji Unicode.

Trololol
źródło
-1

Dodam tylko do starego pytania:

NIE

Rozpocznij nowy projekt CLR C ++ w VS2010. Microsoft sami używają L"Hello World"”- powiedział nuff.

kizzx2
źródło
13
Środowisko CLR to zupełnie inne środowisko niż kod niezarządzany. To nie jest argument.
Cody Grey
3
Nawet Microsoft popełnia błędy.
Pavel Radzivilovsky
6
-1 Pytanie jest oznaczone Ci C++. Odpowiedzi mogą zawsze zostać usunięte przez ich autorów. To byłby dobry moment na skorzystanie z tego przepisu.
Niespodziewane
-1

TCHARmają nowe znaczenie do przenoszenia z WCHARdo CHAR.

https://docs.microsoft.com/en-us/windows/uwp/design/globalizing/use-utf8-code-page

Ostatnie wersje systemu Windows 10 wykorzystywały stronę kodową ANSI i interfejsy API -A jako sposób na wprowadzenie obsługi UTF-8 do aplikacji. Jeśli strona kodowa ANSI jest skonfigurowana dla UTF-8, funkcje API -A działają w UTF-8.

OwnageIsMagic
źródło