Как использовать iconv(3) для преобразования широкой строки в UTF-8?

Я пытаюсь использовать iconv(3) для преобразования строки широких символов в UTF-8, используя приведенный ниже код. Когда я запускаю приведенное ниже, вызов iconv возвращает E2BIG, как будто в выходном буфере недостаточно байтов свободного места. Это происходит, несмотря на то, что (я думаю) размер выходного буфера таков, чтобы допустить наихудшее расширение для UTF-8. На самом деле, учитывая, что ввод представляет собой простую ASCII-символ, закодированную как wchar_t, за которой следует нулевой терминатор wchar_t, вывод должен состоять ровно из двух байтов/символов: «A», за которым следует «\0».

'man utf-8' в моей системе Linux говорит, что максимальная длина последовательности байтов UTF-8 составляет 6 байтов, поэтому я считаю, что для входного буфера из 2 wchar_ts (символ, за которым следует завершающий нуль), что делает (на моя система) всего 8 байт (поскольку sizeof(wchar_t) == 4), буфера в 12 байт (2 * UTF8_SEQUENCE_MAXLEN) должно быть достаточно.

Экспериментально, если я увеличу UTF8_SEQUENCE_MAXLEN до 16, возвращаемое значение iconv указывает на успех (15 по-прежнему не работает). Но я не вижу никакого способа, чтобы любое значение wchar_t занимало столько байтов при кодировании в UTF-8.

Я ошибся в своих расчетах? Возможны ли 16-байтовые последовательности UTF-8? Что я сделал не так?

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

#define UTF8_SEQUENCE_MAXLEN 6
/* #define UTF8_SEQUENCE_MAXLEN 16 */

int
main(int argc, char **argv)
{
    wchar_t *wcs = L"A";
    signed char utf8[(1 /* wcslen(wcs) */ + 1 /* L'\0' */) * UTF8_SEQUENCE_MAXLEN];
    char *iconv_in = (char *) wcs;
    char *iconv_out = (char *) &utf8[0];
    size_t iconv_in_bytes = (wcslen(wcs) + 1 /* L'\0' */) * sizeof(wchar_t);
    size_t iconv_out_bytes = sizeof(utf8);
    size_t ret;
    iconv_t cd;

    cd = iconv_open("WCHAR_T", "UTF-8");
    if ((iconv_t) -1 == cd) {
        perror("iconv_open");
        return EXIT_FAILURE;
    }

    ret = iconv(cd, &iconv_in, &iconv_in_bytes, &iconv_out, &iconv_out_bytes);
    if ((size_t) -1 == ret) {
        perror("iconv");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

person AnotherSmellyGeek    schedule 03.11.2013    source источник


Ответы (1)


Аргументы iconv_open неверны. Порядок аргументов следующий: (к, от), а не (от, к), как четко указано на странице руководства.

Следовательно, изменение

iconv_open("WCHAR_T", "UTF-8");

to

iconv_open("UTF-8", "WCHAR_T");

приводит к тому, что приведенный выше код (в остальном неизменный) работает должным образом.

Д'о. Нужно читать man-страницы более внимательно.

person AnotherSmellyGeek    schedule 03.11.2013