Подтвердить что ты не робот

Печать строк UTF-8 с помощью printf-wide или многобайтовых строковых литералов

В таких выражениях, где оба они вводятся в исходный код с одинаковой кодировкой (UTF-8), и локаль настроена правильно, существует ли какая-либо практическая разница между ними?

printf("ο Δικαιοπολις εν αγρω εστιν\n");
printf("%ls", L"ο Δικαιοπολις εν αγρω εστιν\n");

И, следовательно, есть ли какие-то причины предпочесть друг другу при выполнении вывода? Я полагаю, что второй выполняет немного хуже, но имеет ли он какое-либо преимущество (или недостаток) над многобайтовым литералом?

EDIT: Нет проблем с печатью этих строк. Но я не использую широкие строковые функции, потому что я хочу также использовать printf и т.д. Таким образом, вопрос заключается в том, являются ли эти способы печати различными (учитывая ситуацию, описанную выше), и если да, то у второго есть какие-то преимущества?

EDIT2: Следуя приведенным ниже комментариям, теперь я знаю, что эта программа работает, что, как я думал, не представляется возможным:

int main()
{
    setlocale(LC_ALL, "");
    wprintf(L"ο Δικαιοπολις εν αγρω εστιν\n");  // wide output
    freopen(NULL, "w", stdout);                 // lets me switch
    printf("ο Δικαιοπολις εν αγρω εστιν\n");    // byte output
}

EDIT3. Я провел некоторое исследование, посмотрев, что происходит с этими двумя типами. Возьмите более простую строку:

wchar_t *wides = L"£100 π";
char *mbs = "£100 π";

Компилятор генерирует другой код. Широкая строка:

.string "\243"
.string ""
.string ""
.string "1"
.string ""
.string ""
.string "0"
.string ""
.string ""
.string "0"
.string ""
.string ""
.string " "
.string ""
.string ""
.string "\300\003"
.string ""
.string ""
.string ""
.string ""
.string ""

Пока второе:

.string "\302\243100 \317\200"

И, глядя на кодировки Unicode, второй - это простой UTF-8. Широкое представление символа - UTF-32. Я понимаю, что это будет зависящим от реализации.

Итак, возможно ли широкое представление символов литералов более переносимо? Моя система не будет печатать кодировки UTF-16/UTF-32 напрямую, поэтому она автоматически преобразуется в UTF-8 для вывода.

4b9b3361

Ответ 1

printf("ο Δικαιοπολις εν αγρω εστιν\n");

печатает строковый литерал (const char*, специальные символы представлены как многобайтовые символы). Хотя вы можете увидеть правильный вывод, есть другие проблемы, с которыми вы можете столкнуться, работая с такими символами, отличными от ASCII. Например:

char str[] = "αγρω";
printf("%d %d\n", sizeof(str), strlen(str));

выводит 9 8, так как каждый из этих специальных символов представлен 2 char s.

При использовании префикса L у вас есть литерал, состоящий из широких символов (const wchar_t*) и спецификатора формата %ls, заставляет эти широкие символы преобразовываться в многобайтовые символы (UTF-8). Обратите внимание, что в этом случае локаль должен быть установлен соответствующим образом, иначе это преобразование может привести к недействительности вывода:

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

int main(void)
{
    setlocale(LC_ALL, "");
    printf("%ls", L"ο Δικαιοπολις εν αγρω εστιν");
    return 0;
}

но, хотя некоторые вещи могут усложниться при работе с широкими символами, другие вещи могут стать намного проще и понятнее. Например:

wchar_t str[] = L"αγρω";
printf("%d %d", sizeof(str) / sizeof(wchar_t), wcslen(str));

выводит 5 4, как можно было бы ожидать.

Как только вы решите работать с широкими строками, wprintf можно использовать для прямой печати широких символов. Здесь также стоит отметить, что в случае консоли Windows режим перевода stdout должен быть явно установлен в один из режимов Unicode, вызывая _setmode

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

#include <io.h>
#include <fcntl.h>
#ifndef _O_U16TEXT
  #define _O_U16TEXT 0x20000
#endif

int main()
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    wprintf(L"%s\n", L"ο Δικαιοπολις εν αγρω εστιν");
    return 0;
}