Windows реализовала Unicode раньше, чем большинство других операционных систем. В результате решения Windows для многих проблем отличаются от решений, принятых теми, кто подождал, когда пыль осядет¹. Самым ярким примером этого является использование Windows UCS-2 в качестве кодировки Unicode. Тогда это была кодировка, рекомендованная консорциумом Unicode, потому что Unicode 1.0 поддерживал только 65'536 символов². Консорциум Unicode передумал пять лет спустя, но к тому времени было уже слишком поздно для Windows, которая уже выпустила Win32s, Windows NT 3.1, Windows NT 3.5, Windows NT 3.51 и Windows 95 - все из которых использовали UCS-2³.
Но сегодня мы поговорим о строках формата в стиле
printf
.Windows реализовала Unicode раньше, чем сам язык C. Это означало, что Windows должна была изобрести поддержку Unicode в языке C. Результатом стали такие функции, как
wcscmp
, wcschr
и wprintf
. Что касается строк формата printf
, вот что мы получили в итоге:
- Спецификатор
%s
представляет строку в той же ширине (ANSI/Unicode), что и строка формата. - Спецификатор
%S
представляет строку шириной, противоположной ширине строки формата. - Спецификатор
%hs
представляет узкую (ANSI) строку независимо от ширины строки формата. - Спецификаторы
%ws
и%ls
представляют широкую строку (Unicode) независимо от ширины строки формата.
TCHAR buffer[256]; GetSomeString(buffer, 256); _tprintf(TEXT("The string is %s.\n"), buffer);Если код компилировался как ANSI, результат был:
char buffer[256]; GetSomeStringA(buffer, 256); printf("The string is %s.\n", buffer);А если код компилировался как Unicode, результат был⁴:
wchar_t buffer[256]; GetSomeStringW(buffer, 256); wprintf(L"The string is %s.\n", buffer);Следуя соглашению о том, что
%s
принимает строку с той же шириной, что и сама строка формата, этот код выполняется правильно при компиляции и в формате ANSI и в Unicode. Это также значительно упрощает преобразование существующего кода ANSI в Unicode, так как вы можете продолжать использовать %s
, и он сам преобразуется в то, что нужно.Когда поддержка Unicode официально появилась в C99, комитет по стандартизации C выбрал другую модель для строк формата
printf
:- Спецификаторы
%s
и%hs
представляют узкую строку. - Спецификатор
%ls
представляет широкую строку.
Они решили придерживаться существующей нестандартной модели, чтобы не сломать все программы Windows на планете.
Если вы хотите, чтобы ваш код работал как в средах выполнения, использующих классические правила
printf
Windows, так и в тех, которые используют стандартные правила printf
языка C - вы можете переписать свой код на использование %hs
для узких строк и %ls
для широких строк, и тогда вы получите непротиворечивые результаты независимо от того, была ли строка формата передана в sprintf
или wsprintf
:
#ifdef UNICODE #define TSTRINGWIDTH TEXT("l") #else #define TSTRINGWIDTH TEXT("h") #endif TCHAR buffer[256]; GetSomeString(buffer, 256); _tprintf(TEXT("The string is %") TSTRINGWIDTH TEXT("s\n"), buffer);В ANSI:
char buffer[256]; GetSomeStringA(buffer, 256); printf("The string is %hs\n", buffer);В Unicode:
wchar_t buffer[256]; GetSomeStringW(buffer, 256); wprintf("The string is %ls\n", buffer);Отдельное определение
TSTRINGWIDTH
позволяет вам делать такие вещи:
_tprintf(TEXT("The string is %10") TSTRINGWIDTH TEXT("s\n"), buffer);Поскольку людям нравятся таблицы, то вот вам таблица:
Спецификатор | Классика Windows | Стандарт C | ||
---|---|---|---|---|
%s | printf | char* | char* | ⇐ |
%s | wprintf | wchar_t* | char* | |
%S | printf | wchar_t* | N/A | |
%S | wprintf | char* | N/A | |
%hs | printf | char* | char* | ⇐ |
%hs | wprintf | char* | char* | ⇐ |
%ls | printf | wchar_t* | wchar_t* | ⇐ |
%ls | wprintf | wchar_t* | wchar_t* | ⇐ |
%ws | printf | wchar_t* | N/A | |
%ws | wprintf | wchar_t* | N/A |
Я выделил строки, в которых стандарт C соответствует классическому формату Windows⁵. Если вы хотите, чтобы ваш код работал одинаково при любом соглашении о формате, вам следует придерживаться этих строк.
Примечания:
¹ Можно подумать, что раннее внедрение Unicode даст Windows преимущество первопроходца, но, по крайней мере, в отношении Unicode, это оказалось недостатком первопроходца, потому что все остальные могут сидеть сложа руки и ждать появления более эффективных решений (таких как UTF-8) до начала их усилий по реализации поддержки Unicode.
² Я думаю, они думали, что 65'536 символов должно хватить на всех.
³ Это было позже модернизировано до UTF-16. К счастью, UTF-16 обратно совместим с UCS-2 для кодовых точек, которые представимы в обоих.
⁴ Технически, вариант с Unicode был таким:
unsigned short buffer[256]; GetSomeStringW(buffer, 256); wprintf(L"The string is %s.\n", buffer);потому что тогда ещё не было
wchar_t
как независимого типа. До введения wchar_t
в стандарт C тип wchar_t
был просто синонимом unsigned short
. Изменяющаяся судьба типа wchar_t
имеет свою собственную историю.⁵ Классический формат Windows появился первым, именно поэтому вопрос состоит в том, выбрал ли стандарт C соответствие классическому формату Windows, а не наоборот.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.