#c #iconv
#c #iconv
Вопрос:
Я пытаюсь создать метод, который преобразует строку s-jis в строку utf-8, используя iconv
. Я написал код ниже,
#include <iconv.h>
#include <iostream>
#include <stdio.h>
using namespace std;
#define BUF_SIZE 1024
size_t z = (size_t) BUF_SIZE-1;
bool sjis2utf8( char* text_sjis, char* text_utf8 )
{
iconv_t ic;
ic = iconv_open("UTF8", "SJIS"); // sjis->utf8
iconv(ic , amp;text_sjis, amp;z, amp;text_utf8, amp;z);
iconv_close(ic);
return true;
}
int main(void)
{
char hello[BUF_SIZE] = "hello";
char bye[BUF_SIZE] = "bye";
char tmp[BUF_SIZE] = "something else";
sjis2utf8(hello, tmp);
cout << tmp << endl;
sjis2utf8(bye, tmp);
cout << tmp << endl;
}
и вывод должен быть
hello
bye
но на самом деле,
hello
hello
Кто-нибудь знает, почему происходит это явление? Что не так с моей программой?
Обратите внимание, что «hello» и «bye» — это японские строки s-jis в моей оригинальной программе, но я изменил это, чтобы сделать программу удобной для просмотра.
Комментарии:
1. пожалуйста, обратите внимание, что в вашем примере z уменьшается дважды для каждого преобразованного символа.
2. также вы не должны использовать utf8, вы должны использовать ascii и wcout UCS2 в Windows и UCS4 в Linux.
Ответ №1:
Я думаю, вы неправильно используете iconv
функцию, передавая ей глобальную переменную z
. При первом вызове sjis2utf8
значение z
уменьшается до 0. Второй вызов sjis2utf8
не имеет никакого эффекта (z==0) и оставляет tmp
без изменений.
Из iconv
документации :
size_t iconv (iconv_t cd,
const char* * inbuf, size_t * inbytesleft,
char* * outbuf, size_t * outbytesleft);
Функция iconv преобразует по одному многобайтовому символу за раз, и для каждого преобразования символов она увеличивает *inbuf и уменьшает *inbytesleft на количество преобразованных входных байтов, она увеличивает *outbuf и уменьшает * outbytesleft на количество преобразованных выходных байтов и обновляет состояние преобразования, содержащееся на cd.
Вы должны использовать две отдельные переменные для длин буферов :
size_t il = BUF_SIZE - 1 ;
size_t ol = BUF_SIZE - 1 ;
iconv(ic, amp;text_sjis, amp;il, amp;text_utf8, amp;ol) ;
Затем проверьте возвращаемое значение iconv
и длины буферов для успешного преобразования.
Комментарии:
1. после первого преобразования z переполнится. В c / c он будет обтекаться, если size_t не имеет знака. Если size_t подписан, это приведет к неопределенному поведению.
2. также в вашем втором заявлении… для использования двух переменных iconv преобразует символы BUF_SIZE -1, а не только содержимое строки… в его примере это очевидно, потому что нулевой ограничитель также будет преобразован в 0, и cout остановится в этот момент.
3. Это зависит от
iconv
реализации внутренних проверок. Как вы сказали в предыдущем комментарии: z уменьшается дважды для каждого преобразованного символа , но мы не знаем, как это проверяется и останавливается при достижении 0. Он может переполниться (да, size_t не имеет знака).
Ответ №2:
#include <iconv.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const size_t BUF_SIZE=1024;
class IConv {
iconv_t ic_;
public:
IConv(const char* to, const char* from)
: ic_(iconv_open(to,from)) { }
~IConv() { iconv_close(ic_); }
bool convert(char* input, char* output, size_tamp; out_size) {
size_t inbufsize = strlen(input) 1;// s-jis string should be null terminated,
// if s-jis is not null terminated or it has
// multiple byte chars with null in them this
// will not work, or to provide in other way
// input buffer length....
return iconv(ic_, amp;input, amp;inbufsize, amp;output, amp;out_size);
}
};
int main(void)
{
char hello[BUF_SIZE] = "hello";
char bye[BUF_SIZE] = "bye";
char tmp[BUF_SIZE] = "something else";
IConv ic("UTF8","SJIS");
size_t outsize = BUF_SIZE;//you will need it
ic.convert(hello, tmp, outsize);
cout << tmp << endl;
outsize = BUF_SIZE;
ic.convert(bye, tmp, outsize);
cout << tmp << endl;
}
- на основе ответа Клейста
Ответ №3:
Вы должны указать длину строки ввода в третьем параметре iconv
.
Попробуйте:
//...
int len = strlen(text_sjis);
iconv(ic , amp;text_sjis, amp;len, amp;text_utf8, amp;z);
//...
Ответ №4:
size_t iconv (iconv_t cd,
const char* * inbuf, size_t * inbytesleft,
char* * outbuf, size_t * outbytesleft);
iconv
изменяет значение, на которое указывает inbytesleft
. Итак, после вашего первого запуска z
равно 0. Чтобы исправить это, вы должны использовать calculate длину inbuf
и сохранять ее в локальной переменной перед каждым преобразованием.
Это описано здесь:http://www.gnu.org/s/libiconv/documentation/libiconv/iconv.3.html
И поскольку вы пометили это как C , я бы предложил объединить все в симпатичный маленький класс, насколько я могу судить из документации, вы можете повторно использовать inconv_t
полученные из iconv_open
данные для стольких преобразований, сколько захотите.
#include <iconv.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const size_t BUF_SIZE = 1024;
size_t z = (size_t) BUF_SIZE-1;
class IConv {
iconv_t ic_;
public:
IConv(const char* to, const char* from)
: ic_(iconv_open(to,from)) { }
~IConv() { iconv_close(ic_); }
bool convert(char* input, char* output, size_t outbufsize) {
size_t inbufsize = strlen(input);
return iconv(ic_, amp;input, amp;inbufsize, amp;output, amp;outbufsize);
}
};
int main(void)
{
char hello[BUF_SIZE] = "hello";
char bye[BUF_SIZE] = "bye";
char tmp[BUF_SIZE] = "something else";
IConv ic("UTF8","SJIS");
ic.convert(hello, tmp, BUF_SIZE);
cout << tmp << endl;
ic.convert(bye, tmp, BUF_SIZE);
cout << tmp << endl;
}
Комментарии:
1. Я думаю, что ваш ответ ошибочен, потому что он всегда будет преобразовывать последовательность 1024 символов / байт.
2. Большое спасибо. Ваш код работает хорошо только после того, как я изменил его
const size_t BUF_SIZE
на#define BUF_SIZE
, и это приятно, потому что на самом деле мне приходится конвертировать строку sjis в строку utf8 в других местах.3. @neagoegab: Изменено на использование strlen и передачу outbufsize в качестве аргумента. Я не вижу необходимости создавать локальную переменную и передавать на нее указатель, как вы сделали в своей копии.
4. BUF_SIZE — это константа … ее нельзя изменить. iconv выполняет запись в outbufsize. Также ему понадобится размер преобразованного текста для дальнейших манипуляций.
5. @neagoegab Мой код не изменяет BUF_SIZE, поскольку он передается по значению. Кто сказал, что ему нужна длина преобразованного текста? Многие API просто ожидают C-строки с нулевым завершением.