Не удается прочитать и повторить ввод юникода в C

Я написал следующий код:

#include <stdio.h>
#include <wchar.h>

int main() {
    wchar_t wc[80];
    wscanf(L"%ls", &wc);
    wprintf(L"%ls", wc);
    return 0;
}

вот что я получил в результате

Мой терминал поддерживает Unicode, скомпилированный с использованием gcc 8.2.1 в Linux.


person i cant    schedule 24.01.2019    source источник
comment
Вы не сказали своей библиотеке C использовать текущую локаль, поэтому вместо этого она использует стандартную C/POSIX.   -  person Nominal Animal    schedule 24.01.2019
comment
так как мне это сделать?   -  person i cant    schedule 24.01.2019
comment
у вас не было новой строки в printf   -  person phuclv    schedule 24.01.2019


Ответы (1)


Вот исправленная версия вашей программы:

#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