Конвертировать UTF-16LE в UTF-8 в C

Я использую библиотеку, в которой есть функция, которая возвращает строки результата, закодированные как UTF-16LE (я уверен) в стандартном char *, а также количество байтов в строке. Я хотел бы преобразовать эти строки в UTF-8. Я попробовал решение из этого вопроса: конвертировать UTF-16 в UTF-8 под Windows и Linux, в C, который говорит использовать iconv, однако в результате оба буфера ввода и вывода оказались пустыми. Что мне не хватает?

Мои входные и выходные буферы объявлены и инициализированы следующим образом:

char *resbuff=NULL;
char *outbuff=NULL;
int stringLen;
size_t outbytes=1024;
size_t inbytes;
size_t convResult;
...
//some loop and control code here
...
if (resbuff==NULL) {
    resbuff=(char *)malloc(1024);
    outbuff=(char *)malloc(1024);
}

Затем я вызываю библиотечную функцию, чтобы заполнить отпор данными. Глядя на буфер в отладчике, я вижу данные в буфере. Например, если данные "тестовые", я вижу следующее, глядя на отдельные индексы отпора:

't','\0','e','\0','s','\0','t','\0'

Я полагаю, что это UTF-16LE (другой код, использующий ту же библиотеку, чтобы подтвердить это), и stringlen теперь равен 8. Затем я пытаюсь преобразовать его в UTF-8, используя следующий код:

iconv_t conv;
conv=iconv_open("UTF-8", "UTF-16LE");
inbytes=stringLen;
convResult=iconv(conv,&resbuff,&inbytes,&outbuff,&outbytes); //this does return 0
iconv_close(conv);

В результате как исходящий, так и исходящий оба заканчиваются как нулевые строки.

Обратите внимание, что я объявляю stringlen как int, а не как unsigned long, потому что именно этого ожидает библиотечная функция.

РЕДАКТИРОВАТЬ: я немного подправил свой код в соответствии с ответом Джона Боллинджера ниже, но это не изменило результат.

РЕДАКТИРОВАТЬ 2: В конечном итоге вывод из этого кода будет использоваться в Python, поэтому я думаю, что, хотя это может быть и уродливее, я просто выполню там преобразование строк. Это просто работает.

1 ответ

Вы не показываете объявление или инициализацию переменных stringLen а также outbytesи ваша проблема вполне может лежать там. Тем не менее, это...

Обратите внимание, что я объявляю stringlen как int, а не как unsigned long, потому что именно этого ожидает библиотечная функция.

... очень беспокоит. iconv() функция ожидает, что ее третий и пятый аргументы будут иметь тип size_t *и ложь компилятору через приведение не заставит код фактически работать, если они на самом деле разные типы. Вы должны иметь что-то вроде этого:

size_t in_bytes_left = (expression giving the total input length, in bytes);
size_t out_bytes_available = (expression giving the size of the output buffer);
char *input_temp = resbuff;
char *output_temp = outbuff;
int result;

result = iconv(conv, &input_temp, &in_bytes_left, &output_temp, &out_bytes_available);

Также обратите внимание, что вы должны проверить возвращаемое значение, чтобы убедиться, что преобразование было завершено и успешно (в этом случае возвращаемое значение будет>= 0). Если оно меньше нуля, то значение errno Сразу после звонка вам скажут, что за проблема возникла.

Отредактировано, чтобы добавить:

Вы изначально сказали, что нулевые байты были преобразованы, но теперь вы говорите, что

outbuff и resbuff оба заканчиваются как нулевые строки.

что совсем не одно и то же.

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

Дополнительно, iconv() возвращает либо индикатор ошибки, либо количество необратимо преобразованных символов, а не общее количество преобразованных символов. Для действительных UTF-16{,LE,BE} в UTF-8 никогда не должно быть никаких необратимых преобразований. Возвращаемое значение ноль указывает, что указанное количество входных байтов было все успешно и обратимо преобразовано в выходные байты.

Обратите внимание, что resbuffпо крайней мере, никогда не был строкой Си. Нулевые символы, внедренные в данные, делают интерпретацию строки неуместной. Однако, в зависимости от того, как были инициализированы ваши входные и выходные буферы, после iconv() отделки, *resbuff == '\0' а также *outbuff == '\0' (ссылаясь на ваш собственный текущий код). Кстати, я бы назвал эти "пустые" строки не "нулевыми". Если вы действительно имеете в виду, что iconv() листья resbuff == 0 а также outbuff == 0 (т.е. указатели NULL), то это будет ошибка в iconv(),

Другие вопросы по тегам