Я пытаюсь понять, как printf работает с широкими символами ( wchar_t
).
Я сделал следующие примеры кода:
#include #include int main(void) { wchar_t *s; s = (wchar_t *)malloc(sizeof(wchar_t) * 2); s[0] = 42; s[1] = 0; printf("%ls\n", s); free(s); return (0); }
выход :
*
Здесь все хорошо: мой персонаж ( *
) отображается правильно.
Я хотел показать другой характер. В моей системе wchar_t
кажется закодированным на 4 байта. Поэтому я попытался отобразить следующий символ: É
#include #include int main(void) { wchar_t *s; s = (wchar_t *)malloc(sizeof(wchar_t) * 2); s[0] = 0xC389; s[1] = 0; printf("%ls\n", s); free(s); return (0); }
Но на этот раз выхода нет, я попытался использовать множество значений из раздела «кодирование» (см. Предыдущую ссылку) для s[0]
(0xC389, 201, 0xC9) … Но я никогда не получаю символ É
. Я также попытался использовать %S
вместо %ls
.
Если я попытаюсь вызывать printf следующим образом: printf("\n", s)
единственный напечатанный символ – '<'
, экран усечен.
Почему у меня такая проблема? Как мне это сделать?
Убедитесь, что вы проверили errno
и возвращаемое значение printf
!
#include #include #include int main(void) { wchar_t *s; s = (wchar_t *) malloc(sizeof(wchar_t) * 2); s[0] = 0xC389; s[1] = 0; if (printf("%ls\n", s) < 0) { perror("printf"); } free(s); return (0); }
См. Вывод:
$ gcc test.c && ./a.out printf: Invalid or incomplete multibyte or wide character
Прежде всего, языковой стандарт по умолчанию программы C - это C
(также известный как POSIX
), который является ASCII-only. Вам нужно будет добавить вызов setlocale
, в частности setlocale(LC_ALL,"")
.
Если переменные среды LC_ALL
, LC_CTYPE
или LANG
не установлены, чтобы разрешить UTF-8, когда они пусты, вам нужно будет явно выбрать локаль. setlocale(LC_ALL, "C.UTF-8")
работает на большинстве систем - C
является стандартным, а подмножество UTF-8
C
обычно реализуется.
#include #include #include #include int main(void) { wchar_t *s; s = (wchar_t *) malloc(sizeof(wchar_t) * 2); s[0] = 0xC389; s[1] = 0; setlocale(LC_ALL, ""); if (printf("%ls\n", s) < 0) { perror("printf"); } free(s); return (0); }
См. Вывод:
$ gcc test.c && ./a.out 쎉
Причина, по которой напечатан неправильный символ, заключается в том, что wchar_t
представляет собой широкий символ (например, UTF-32), а не многобайтовый символ (например, UTF-8). Обратите внимание, что wchar_t
всегда имеет ширину в 32 бита в библиотеке GNU C, но стандарт C не требует этого. Если вы инициализируете символ с использованием UTF-32BE
(то есть 0x000000C9
), то он печатает правильно:
#include #include #include #include int main(void) { wchar_t *s; s = (wchar_t *) malloc(sizeof(wchar_t) * 2); s[0] = 0xC9; s[1] = 0; setlocale(LC_ALL, ""); if (printf("%ls\n", s) < 0) { perror("printf"); } free(s); return (0); }
Выход:
$ gcc test.c && ./a.out É
Обратите внимание, что вы также можете установить переменные среды LC
(locale) через командную строку:
$ LC_ALL=C.UTF-8 $ ./a.out É
Одна из проблем заключается в том, что вы пытаетесь кодировать UTF-8, которая является однобайтовой схемой кодирования, как многобайтовая кодировка. Для UTF-8 используется простой char
.
Также обратите внимание, что, поскольку вы пытаетесь объединить последовательность UTF-8 в многобайтовый тип, у вас есть проблемы с порядком (байтом) (в памяти 0xC389
может храниться как 0x89
и 0xC3
, в этом порядке). И что компилятор подпишет – также расширьте свой номер (если sizeof(wchar_t) == 4
и вы посмотрите на s[0]
в отладчике, это может быть 0xFFFFC389
).
Другая проблема – это терминал или консоль, которые вы используете для печати. Может быть, это просто не поддерживает UTF-8 или другие кодировки, которые вы пробовали?