4

UTF-16 文字の文字列を出力しようとしています。しばらく前にこの質問を投稿しましたが、iconv を使用して UTF-32 に変換し、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 文字列が OK を出力する場合があります。また、wchar_t の出力中に問題が発生したように見え、printf 関数呼び出しを完全に終了させて​​、末尾の「***」も出力されないようにすることもあります。

私も使ってみました

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

しかし、何も印刷されません。

ここで何か不足していますか?

参考:CでUTF-16文字を出力するには?

アップデート

コメントにいくつかの提案を組み込みました。

更新されたコード:

// *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 バイトであることを確認しました。

4

1 に答える 1

5

以下は、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再度呼び出します。

于 2011-12-12T21:26:42.533 に答える