Чи залишається актуальним TCHAR?


87

Я новачок у програмуванні Windows, і після прочитання книги Петцольда мені цікаво:

чи все ще є гарною практикою використовувати TCHARтип і _T()функцію для оголошення рядків, або якщо я повинен просто використовувати рядки wchar_tі L""в новому коді?

Я націлюся лише на Windows 2000 та новіші версії, і мій код буде i18n з самого початку.

Відповіді:


15

Я б як і раніше використовував синтаксис TCHAR, якби сьогодні робив новий проект. Існує не так багато практичних різниць між його використанням та синтаксисом WCHAR, і я віддаю перевагу коду, який є явним у тому, який тип символу. Оскільки більшість функцій API та допоміжних об'єктів беруть / використовують типи TCHAR (наприклад: CString), просто має сенс використовувати його. Плюс це дає вам гнучкість, якщо ви вирішите в якийсь момент використовувати код у програмі ASCII, або якщо Windows коли-небудь перетвориться на Unicode32 тощо.

Якщо ви вирішите поїхати за маршрутом WCHAR, я б чітко про це сказав. Тобто, використовуйте CStringW замість CString та призначайте макроси під час перетворення в TCHAR (наприклад: CW2CT).

Це моя думка, так чи інакше.


Дійсно, це все одно буде працювати, коли кодування символів врешті-решт буде змінено `` знову ''.
Medinoc

11
Ви віддаєте перевагу коду, який є явним у тому, який тип символу, і, отже, використовуєте тип, який іноді такий, а іноді той? Дуже переконливо.
Дедулікатор

4
-1 за невідповідність, зазначену @Deduplicator, і за негативну пораду щодо виплат використовувати макрос, який може бути будь-яким (і, як правило, не перевіряється для більш ніж одного конкретного значення).
Вітаю і hth. - Альф

90

Коротка відповідь: НІ .

Як і всі інші, про які ми вже писали, багато програмістів все ще використовують TCHAR і відповідні функції. На мою скромну думку, вся концепція була поганою ідеєю . Обробка рядків UTF-16 значно відрізняється від простої обробки рядків ASCII / MBCS. Якщо ви використовуєте однакові алгоритми / функції з обома (на цьому заснована ідея TCHAR!), Ви отримуєте дуже погану продуктивність у версії UTF-16, якщо ви робите трохи більше, ніж просте об'єднання рядків (наприклад, розбір тощо). Основна причина - Сурогати .

За єдиним винятком, коли вам дійсно потрібно скласти свою заявку для системи, яка не підтримує Unicode, я не бачу причин використовувати цей багаж минулого в новій програмі.


6
Цікавий факт: UTF-16 не завжди був там на платформі NT. Сурогатні кодові точки були введені з Unicode 2.0 в 1996 році, того ж року, коли вийшов NT 4. До IIRC, (включаючи) Windows 2000, усі версії NT використовували UCS-2, фактично підмножину UTF-16, яка передбачала, що кожен символ можна представляти з однією кодовою точкою (тобто без сурогатів).
0xC0000022L

3
До речі, хоча я погоджуюсь, що більше TCHARне слід використовувати, я не згоден з тим, що це була погана ідея. Я також думаю, що якщо ви вирішите бути явним, а не використовувати, TCHARви повинні бути явними скрізь . Тобто в їх декларації також не використовувати функції з TCHAR/ _TCHAR(наприклад, _tmain). Простіше кажучи: будьте послідовними. +1, все ще.
0xC0000022L

3
Це була гарна ідея ще при її введенні, але вона не повинна мати значення у новому коді.
Адріан Маккарті

4
Ви спотворюєте те, для чого TCHARспочатку були введені: щоб полегшити розробку коду для версій Windows 9 на основі Win 9x та Windows NT. На той час реалізація UTF-16 для Windows NT була UCS-2, а алгоритми синтаксичного аналізу / обробки рядків були однаковими. Сурогатів не було. Навіть із сурогатними алгоритмами алгоритми DBCS (єдине підтримуване кодування MBCS для Windows) та UTF-16 однакові: У кожному кодуванні кодова точка складається з однієї або двох кодових одиниць.
IIНеогляд

Припустимо, я хочу використовувати FormatMessage () для перетворення значення з WSAGetLastError () у щось для друку. У документації до WSAGetLastError () сказано, що LPTSTR використовується як вказівник на буфер. У мене справді немає великого вибору, крім як використовувати TCHAR, ні?
Едвард Фальк,

80

Я повинен погодитися з Сашею. Основна передумова TCHAR/ _T()/ тощо полягає в тому, що ви можете написати додаток на основі "ANSI", а потім чарівним чином надати йому підтримку Unicode, визначивши макрос. Але це ґрунтується на декількох поганих припущеннях:

Що ви активно створюєте версії свого програмного забезпечення як MBCS, так і Unicode

В іншому випадку ви будете проскакувати і використовувати звичайні char*струни в багатьох місцях.

Те, що ви не використовуєте символи зворотної косої риски, що не є ASCII, у літералах _T ("...")

Якщо ваше кодування "ANSI" не відповідає ISO-8859-1, результуючі char*та wchar_t*літерали не будуть представляти однакові символи.

Ці рядки UTF-16 використовуються так само, як рядки "ANSI"

Вони ні. Unicode представляє кілька концепцій, яких немає в більшості застарілих кодувань символів. Сурогати. Поєднання символів. Нормалізація. Умовні та чутливі до мови правила обкладинки.

І, мабуть, найголовніше, той факт, що UTF-16 рідко зберігається на диску або надсилається через Інтернет: UTF-8, як правило, є кращим для зовнішнього представлення.

Що ваша програма не використовує Інтернет

(Зараз це може бути припустимим припущенням для вашого програмного забезпечення, але ...)

Мережа працює на UTF-8 та безлічі рідкісних кодувань . TCHARКонцепція визнає тільки два: "ANSI" (який не може бути UTF-8 ) і "Unicode" (UTF-16). Це може бути корисно для того, щоб зробити ваш Windows API викликом Unicode, але він проклятий марним для того, щоб зробити ваші веб-програми та програми електронної пошти Unicode.

Що ви не використовуєте бібліотеки, що не належать Microsoft

Більше ніхто не використовує TCHAR. Poco використовує std::stringта UTF-8. SQLite має версії свого API UTF-8 та UTF-16, але ні TCHAR. TCHARнавіть немає у стандартній бібліотеці, тому ніstd::tcout якщо ви не хочете визначити його самостійно.

Що я рекомендую замість TCHAR

Забудьте, що існують кодування "ANSI", за винятком випадків, коли вам потрібно прочитати файл, який не є дійсним UTF-8. Забудь про TCHARтеж. Завжди викликайте «W» версію функцій Windows API. #define _UNICODEлише для того, щоб випадково не викликати функцію "А".

Завжди використовуйте кодування UTF для рядків: UTF-8 для charрядків та UTF-16 (у Windows) або UTF-32 (у Unix-подібних системах) для wchar_tрядків. typedef UTF16і UTF32типи символів, щоб уникнути відмінностей на платформі.


6
Виклик 2012 року: є додатки, які потрібно підтримувати #define _UNICODEнавіть зараз. Кінець передачі :)
0xC0000022L

12
@ 0xC0000022L питання стосувалось нового коду. Коли ви підтримуєте старий код, вам, очевидно, доведеться працювати з середовищем , для якого написаний код. Якщо ви підтримуєте програму COBOL, то не має значення, хороша мова COBOL чи ні, ви застрягли в ній. І якщо ви підтримуєте програму, яка покладається на TCHAR, то неважливо, чи було це прийнятне рішення чи ні, ви застрягли в ньому.
jalf

2
Дійсно, TCHAR не корисний, якщо не в COBOL)
Павло Радзивіловський

1
_UNICODEконтролює, як узагальнені текстові зіставлення вирішуються в ЕПТ. Якщо ви не хочете викликати ANSI-версію Windows API, вам потрібно визначити UNICODE.
IIСпецифічно

18

Якщо вам цікаво, чи це все ще на практиці, то так - це все ще використовується досить часто. Ніхто не буде дивитись на ваш код смішно, якщо він використовує TCHAR та _T (""). Проект, над яким я зараз працюю, перетворює з ANSI на Unicode - і ми йдемо портативним маршрутом (TCHAR).

Однак ...

Моїм голосом було б забути всі портативні макроси ANSI / UNICODE (TCHAR, _T ("") і всі дзвінки _tXXXXXX тощо ...) і просто взяти Unicode скрізь. Я справді не бачу сенсу бути портативним, якщо вам ніколи не знадобиться версія ANSI. Я б використовував усі широкі функції та типи символів безпосередньо. Попередньо додайте всі рядкові літерали символом L.


3
Ви можете написати якийсь код, який вам потрібно буде використовувати десь ще, де вам потрібна версія ANSI, або (як сказав Нік) Windows може перейти на DCHAR або що завгодно, тому я все ще вважаю, що дуже гарною ідеєю є використовувати TCHAR замість WCHAR.
arke

Я сумніваюся, що Windows коли-небудь перейде на UTF-32.
dan04

7
-1 для рекомендації UTF-16. Це не лише створює непереносний (орієнтований на Windows) код, що є неприйнятним для бібліотек - навіть незважаючи на те, що він може використовуватися в найпростіших випадках, таких як код інтерфейсу користувача, - це не ефективно навіть у самій Windows. utf8everywhere.org
Павло Радзивіловський

11

У статті " Вступ до програмування Windows" про MSDN сказано

Нові програми завжди повинні викликати версії Unicode (API).

TEXT і TCHAR макроси менш корисні сьогодні, оскільки всі додатки повинні використовувати Unicode.

Я б дотримувався wchar_tі L"".


4
Стівене, ти цитуєш текст, написаний кимось, хто не розуміє значення слова "Юнікод". Це один із тих нещасних документів часів плутанини UCS-2.
Павло Радзивіловський

2
@PavelRadzivilovsky: Документ написаний для системи, де Unicode та UTF-16LE зазвичай використовуються як взаємозамінні. Хоча технічно неточний, проте він однозначний. На це також чітко зазначається у введенні того самого тексту: "Windows представляє символи Unicode із використанням кодування UTF-16 [...]" .
IIСпецифічно

11

Я хотів би запропонувати інший підхід (жоден з двох).

Підсумовуючи, використовуйте char * і std :: string, припускаючи кодування UTF-8, і виконуйте перетворення в UTF-16 лише під час обтікання функцій API.

Більше інформації та обґрунтування цього підходу в програмах Windows можна знайти на веб-сайті http://www.utf8everywhere.org .


@PavelRadzivilovsky, реалізовуючи вашу пропозицію у додатку VC ++, чи встановимо для параметра VC ++ значення "None" або "Multibyte (MBCS)"? Причиною, з якою я запитую, є те, що я щойно встановив Boost :: Locale, а набір символів за замовчуванням - MBCS. FWIW, для мого чистого ASCII-додатка було встановлено значення "None", і тепер я встановив його для "MBCS" (оскільки я буду використовувати Boost :: Locale), і він працює чудово. Порадьте, будь ласка.
Caroline Beltran

Як рекомендує utf8everywhere, я б встановив для нього значення "Використовувати набір символів Unicode". Це додає додаткової безпеки, але не є обов’язковим. Автор Boost :: locale дуже розумний хлопець, я впевнений, що він вчинив правильно.
Павло Радзивіловський

1
UTF-8 Скрізь мантра НЕ буде правильним рішенням, тільки тому , що вона повторюється частіше. UTF-8, безсумнівно, є привабливим кодуванням для серіалізації (наприклад, файли або мережеві сокети), але в Windows часто доцільніше зберігати символьні дані за допомогою власного кодування UTF-16 всередині та конвертувати на межі програми. Одна з причин полягає в тому, що UTF-16 - це єдине кодування, яке можна негайно перетворити на будь-яке інше підтримуване кодування. Це не стосується UTF-8.
IIНевидимий

"..UTF-16 - це єдине кодування, яке можна негайно перетворити на будь-яке інше підтримуване кодування." Що ви маєте на увазі? У чому проблема перетворити кодування UTF-8 на щось інше?
Павло Радзивіловський

1
Я не розумію. До чогось іншого - як до чого? Наприклад, UCS-4? Чому ні? Здається дуже легким, все числовий алгоритм ..
Павло Радзивіловський

7

TCHAR/ WCHARможе бути достатньо для деяких старих проектів. Але щодо нових додатків я б сказав НІ .

Всі ці TCHAR/ WCHARречі є в силу історичних причин. TCHARзабезпечує начебто акуратний спосіб (маскування) для переключення між кодуванням тексту ANSI (MBCS) та кодуванням тексту Unicode (UTF-16). У минулому люди не розуміли кількості символів усіх мов світу. Вони вважали, що 2 байт було достатньо для представлення всіх символів і, отже, маючи схему кодування символів фіксованої довжини WCHAR. Однак це вже не відповідає дійсності після випуску Unicode 2.0 у 1996 році .

Тобто: незалежно від того, що ви використовуєте в CHAR/ WCHAR/ TCHAR, частина обробки тексту у вашій програмі повинна мати можливість обробляти символи змінної довжини для інтернаціоналізації.

Отже, насправді вам потрібно зробити більше, ніж вибрати один із CHAR/ WCHAR/ TCHARдля програмування в Windows:

  1. Якщо ваша програма невелика і не передбачає обробки тексту (тобто просто передавання текстового рядка як аргументів), тоді дотримуйтесь WCHAR. Оскільки таким способом простіше працювати з WinAPI з підтримкою Unicode.
  2. В іншому випадку я б запропонував використовувати UTF-8 як внутрішнє кодування та зберігати тексти у символьних рядках або std :: string. І приховайте їх до UTF-16 під час виклику WinAPI. Зараз UTF-8 є домінуючим кодуванням, і існує безліч зручних бібліотек та інструментів для обробки рядків UTF-8.

Перегляньте цей чудовий веб-сайт для більш детального читання: http://utf8everywhere.org/


2
"UTF-8 тепер є домінуючим кодуванням" - це виявилося неправильним, залишивши другу частину цитати ( "для Всесвітньої павутини" ). Для настільних програм найбільш часто використовується кодування власних символів, як і раніше, UTF-16. Windows використовує це, Mac OS X - теж, а також .NET та Java. На це припадає величезна кількість коду. Не зрозумійте мене неправильно, в UTF-8 немає нічого поганого для серіалізації. Але частіше за все (особливо у Windows) ви виявите, що внутрішнє використання UTF-16 є більш доцільним.
IIСпецифічно

4

Так, абсолютно; принаймні для макросу _T. Однак я не настільки впевнений у широкоформатних речах.

Причиною є краща підтримка WinCE або інших нестандартних платформ Windows. Якщо ви на 100% впевнені, що ваш код залишиться на NT, тоді ви, ймовірно, можете просто використовувати звичайні декларації C-рядків. Однак найкраще схилятися до більш гнучкого підходу, оскільки набагато простіше #define той макрос на платформі, що не є Windows, порівняно з переглядом тисяч рядків коду та додаванням його скрізь, якщо вам потрібно перенести якусь бібліотеку на мобільний.


1
WinCE використовує 16-розрядні рядки wchar_t так само, як Win32. У нас є велика база коду, який працює на WinCE та Win32, і ми ніколи не використовуємо TCHAR.
mhenry1384

2

ІМХО, якщо у вашому коді є TCHAR, ви працюєте на неправильному рівні абстракції.

Використовуйте будь-який тип рядка, який є найбільш зручним для вас, коли маєте справу з обробкою тексту - сподіваємось, це буде щось, що підтримує Unicode, але це залежить від вас. За необхідності виконайте перетворення на межі API API.

Коли ви маєте справу із шляхами файлів, замість рядків використовуйте власний тип. Це дозволить вам незалежні від ОС роздільники шляхів, дасть вам простіший інтерфейс для кодування, ніж ручне об’єднання та розбиття рядків, і буде набагато легше адаптувати до різних ОС (ansi, ucs-2, utf-8, що завгодно) .


Unicode має принаймні три поточні кодування (UTF-8, UTF-16, UTF-32) і одне застаріле кодування (UCS-2, підмножина того, що зараз є UTF-16). До якого з них ви маєте на увазі? Мені подобаються решта пропозицій, хоча +1
0xC0000022L

2

Єдині причини, за якими я бачу використовувати щось, крім явного WCHAR, - це портативність та ефективність.

Якщо ви хочете зробити свій остаточний виконуваний файл якомога меншим, використовуйте char.

Якщо ви не дбаєте про використання оперативної пам’яті і хочете, щоб інтернаціоналізація була такою ж простою, як простий переклад, скористайтеся WCHAR.

Якщо ви хочете зробити свій код гнучким, використовуйте TCHAR.

Якщо ви плануєте використовувати лише латинські символи, ви можете також використовувати рядки ASCII / MBCS, щоб користувачеві не потрібно було стільки оперативної пам'яті.

Для людей, які "i18n з самого початку", заощаджуйте собі простір вихідного коду і просто використовуйте всі функції Unicode.


-1

Просто додаю до старого питання:

НЕМАЄ

Почніть новий проект CLR C ++ у VS2010. Самі Microsoft використовують L"Hello World", - сказав Нафф.


13
CLR - це зовсім інше середовище, ніж некерований код. Це не аргумент.
Коді Грей

3
Навіть Microsoft робить помилки.
Павло Радзивіловський

6
-1 Питання позначене Cта C++. Відповіді завжди можуть бути видалені відповідними авторами. Це був би сприятливий час для використання цього положення.
IIСпецифічно

-1

TCHARмають нове значення портувати з WCHARдо CHAR.

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

Останні випуски Windows 10 використовували кодову сторінку ANSI та API API як засіб для впровадження підтримки програм UTF-8. Якщо кодова сторінка ANSI налаштована для UTF-8, API API працює в UTF-8.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.