Как конвертировать UTF-16 в UTF-32 и распечатать результат wchar_t в C? - PullRequest
4 голосов
/ 11 декабря 2011

Я пытаюсь распечатать строку символов UTF-16. Я отправил этот вопрос некоторое время назад, и был дан совет, чтобы преобразовать в UTF-32 с помощью iconv и распечатать его как строку wchar_t.

Я провел некоторое исследование и сумел написать следующее:

// *c is the pointer to the characters (UTF-16) i'm trying to print
// sz is the size in bytes of the input i'm trying to print

iconv_t icv;
char in_buf[sz];
char* in;
size_t in_sz;
char out_buf[sz * 2];
char* out;
size_t out_sz;

icv = iconv_open("UTF-32", "UTF-16");

memcpy(in_buf, c, sz);

in = in_buf;
in_sz = sz;
out = out_buf;
out_sz = sz * 2;

size_t ret = iconv(icv, &in, &in_sz, &out, &out_sz);
printf("ret = %d\n", ret);
printf("*** %ls ***\n", ((wchar_t*) out_buf));

Вызов iconv всегда возвращает 0, поэтому я думаю, что преобразование должно быть в порядке?

Тем не менее, печать кажется удачной. Время от времени преобразованная строка wchar_t печатает ОК. В других случаях кажется, что при печати wchar_t возникает проблема, и он вообще завершает вызов функции printf, так что даже завершающий символ "***" не печатается.

Я также пытался использовать

wprintf(((wchar_t*) "*** %ls ***\n"), out_buf));

но ничего не печатается.

Я что-то здесь упускаю?

Ссылка: Как напечатать символы UTF-16 в C?

UPDATE

включены некоторые предложения в комментариях.

обновленный код:

// *c is the pointer to the characters (UTF-16) i'm trying to print
// sz is the size in bytes of the input i'm trying to print

iconv_t icv;
char in_buf[sz];
char* in;
size_t in_sz;
wchar_t out_buf[sz / 2];
char* out;
size_t out_sz;

icv = iconv_open("UTF-32", "UTF-16");

memcpy(in_buf, c, sz);

in = in_buf;
in_sz = sz;
out = (char*) out_buf;
out_sz = sz * 2;

size_t ret = iconv(icv, &in, &in_sz, &out, &out_sz);
printf("ret = %d\n", ret);
printf("*** %ls ***\n", out_buf);
wprintf(L"*** %ls ***\n", out_buf);

все тот же результат, не все строки UTF-16 печатаются (и printf, и wprintf).

что еще я мог пропустить?

Кстати, я использую Linux и проверил, что wchar_t составляет 4 байта.

1 Ответ

4 голосов
/ 13 декабря 2011

Вот короткая программа, которая преобразует UTF-16 в массив широких символов и затем распечатывает его.

#include <endian.h>
#include <errno.h>
#include <iconv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#define FROMCODE "UTF-16"

#if (BYTE_ORDER == LITTLE_ENDIAN)
#define TOCODE "UTF-32LE"
#elif (BYTE_ORDER == BIG_ENDIAN)
#define TOCODE "UTF-32BE"
#else
#error Unsupported byte order
#endif

int main(void)
{
    void *tmp;
    char *outbuf;
    const char *inbuf;
    long converted = 0;
    wchar_t *out = NULL;
    int status = EXIT_SUCCESS, n;
    size_t inbytesleft, outbytesleft, size;
    const char in[] = {
        0xff, 0xfe,
        'H', 0x0,
        'e', 0x0,
        'l', 0x0,
        'l', 0x0,
        'o', 0x0,
        ',', 0x0,
        ' ', 0x0,
        'W', 0x0,
        'o', 0x0,
        'r', 0x0,
        'l', 0x0,
        'd', 0x0,
        '!', 0x0
    };
    iconv_t cd = iconv_open(TOCODE, FROMCODE);
    if ((iconv_t)-1 == cd) {
        if (EINVAL == errno) {
            fprintf(stderr, "iconv: cannot convert from %s to %s\n",
                    FROMCODE, TOCODE);
        } else {
            fprintf(stderr, "iconv: %s\n", strerror(errno));
        }
        goto error;
    }
    size = sizeof(in) * sizeof(wchar_t);
    inbuf = in;
    inbytesleft = sizeof(in);
    while (1) {
        tmp = realloc(out, size + sizeof(wchar_t));
        if (!tmp) {
            fprintf(stderr, "realloc: %s\n", strerror(errno));
            goto error;
        }
        out = tmp;
        outbuf = (char *)out + converted;
        outbytesleft = size - converted;
        n = iconv(cd, (char **)&inbuf, &inbytesleft, &outbuf, &outbytesleft);
        if (-1 == n) {
            if (EINVAL == errno) {
                /* junk at the end of the buffer, ignore it */
                break;
            } else if (E2BIG != errno) {
                /* unrecoverable error */
                fprintf(stderr, "iconv: %s\n", strerror(errno));
                goto error;
            }
            /* increase the size of the output buffer */
            converted = size - outbytesleft;
            size <<= 1;
        } else {
            /* done */
            break;
        }
    }
    converted = (size - outbytesleft) / sizeof(wchar_t);
    out[converted] = L'\0';
    fprintf(stdout, "%ls\n", out);
    /* flush the iconv buffer */
    iconv(cd, NULL, NULL, &outbuf, &outbytesleft);
exit:
    if (out) {
        free(out);
    }
    if (cd) {
        iconv_close(cd);
    }
    exit(status);
error:
    status = EXIT_FAILURE;
    goto exit;
}

Поскольку UTF-16 является кодировкой переменной длины, вы угадываете, насколько велика вашавыходной буфер должен быть.Правильная программа должна обрабатывать случай, когда выходной буфер недостаточно велик для хранения преобразованных данных.

Вы также должны заметить, что iconv не NULL определяет ваш выходной буфер для вас.

Iconv является потоково-ориентированным процессором, поэтому вам нужно сбросить iconv_t, если вы хотите использовать его для другого преобразования (пример кода делает это ближе к концу).Если вы хотите выполнить потоковую обработку, вы бы обработали ошибку EINVAL, скопировав все байты, оставшиеся во входном буфере, в начало нового входного буфера перед повторным вызовом iconv.

...