Вот исправленная версия вашей программы:
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
int main(void)
{
wchar_t ws[80];
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: The C library does not support your current locale.\n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: The C library does not support wide standard input for your current locale.\n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: The C library does not support wide standard output for your current locale.\n");
if (wscanf(L"%79ls", ws) < 1) {
fprintf(stderr, "No input.\n");
exit(EXIT_FAILURE);
}
wprintf(L"%ls\n", ws);
return EXIT_SUCCESS;
}
Вызов setlocale()
указывает библиотеке C использовать текущую настроенную локаль. Если вы этого не сделаете, библиотека C использует свою локаль по умолчанию (C/POSIX), которая обычно использует набор символов ASCII (а не UTF-8).
Вызовы fwide(stdin, 1)
и fwide(stdout, 1)
сообщают библиотеке C, которую вы будете использовать. широкие функции ввода со стандартным вводом и широкие функции вывода со стандартным выводом. Они вернут -1, если библиотека C не поддерживает его для текущей локали; Я считаю, что в настоящее время это происходит с локалями на основе UTF-8 в Windows, потому что Microsoft хочет, чтобы программисты вместо этого использовали свои проприетарные расширения.
Вызовы fwide()
не требуются, потому что библиотека C будет угадывать на основе первой функции, которую вы используете для каждого потока. Я рекомендую использовать их явно, чтобы пользователь знал, есть ли что-то сомнительное/неправильное/неподдерживаемое в их текущей конфигурации или поддержке библиотеки C. В конце концов, это всего лишь пара добавленных строк.
При сканировании строк вы всегда должны включать в шаблон максимально допустимую длину (сразу после %
). Поскольку строки C имеют завершающий нулевой символ ('\0'
для узких строк и L'\0'
для широких строк), буфер должен быть как минимум на единицу длиннее. Поскольку ws
представляет собой массив из 80 широких символов, wscanf()
может сканировать в него строку длиной до 79 символов.
Все функции сканирования (scanf()
, wscanf()
, fscanf()
, fwscanf()
и т. д.) возвращают количество успешных преобразований или EOF/WEOF. Например, если пользователь запускает true | ./thisprogram
, в стандартном вводе нет ввода, и вызов wscanf()
вернет WEOF. За исключением пары редких исключений (использование подавленных преобразований для потребления/пропуска данных или преобразований с использованием %n
), вам необходимо проверить возвращаемое значение. Если вы не проверите возвращаемое значение в приведенном выше примере (true | ./thisprogram
), вы в конечном итоге напечатаете неинициализированный буфер широких символов. Что нехорошо; он может либо ничего не печатать, либо печатать мусор, либо завершать работу программы: это Undefined Behavior.
(Также важно помнить, что если преобразование завершается неудачей, неудачная часть остается во входных данных, она не потребляется и не выбрасывается. Она просто остается там, пока вы ее не используете.)
Некоторые оболочки добавляют символ %
в конце последней строки вывода, если эта строка не заканчивается новой строкой. Другие оболочки помещают свою подсказку сразу после этого. Это не ошибка, просто выглядит странно. Таким образом, рекомендуется всегда добавлять новую строку в конце ваших выходных данных.
Стандартный вывод также по умолчанию буферизуется строкой. Например, в приведенной выше программе использование wprintf(L"foo")
не означает, что выводится широкая строка foo
; обычно он просто буферизуется стандартной библиотекой C и выводится через некоторое время. Вы можете указать стандартной библиотеке выводить все в свой буфер для определенного потока, например стандартный вывод, используя fflush(stdout);
. Это относится как к обычным/узким, так и к широким потокам. Однако библиотека C автоматически очищает буферы при выходе из программы.
person
Nominal Animal
schedule
24.01.2019