LINUX.ORG.RU

c и iconv - помогите разобраться


0

0

Здравствуйте.
Пытаюсь простой пример выполнить - не хочет работаь. На выходе пустая строка. Что не так с кодом, где ошибка?

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

char* convert(const char* s, const char* from_cp, const char* to_cp)
{
	char* t = (char*)malloc(strlen(to_cp));
	strcpy(t, to_cp);

	iconv_t ic = iconv_open(strcat(t, "//IGNORE"), from_cp);

	if (ic == (iconv_t)(-1)) {
		fprintf(stderr, "iconv: cannot convert from %s to %s\n", from_cp, to_cp);
		return "";
	}

	char* out = (char*)calloc(strlen(s), sizeof(char));

	char* in = (char*)malloc(strlen(s));
	strcpy(in, s);

	size_t in_ln = strlen(s);
	size_t out_ln = strlen(s);
	size_t k = iconv(ic, &in, &in_ln, &out, &out_ln);
	if(k < strlen(s))
		fprintf(stderr, "iconv: %d of %d converted\n", k, strlen(s));

	fprintf(stderr, "iconv: %s\n", strerror(errno));

	iconv_close(ic);

	free(t);

	return out;
}

int main()
{
	char* s = convert("Тестовая строка", "utf-8", "cp1251");
	printf("CP1251: %s\n", s);
	return EXIT_SUCCESS;
}

>> char* t = (char*)malloc(strlen(to_cp));

char* t = (char*)malloc(strlen(to_cp) + strlen("//IGNORE") /* for strcat */ + 1 /* '\0' */);

>> iconv_t ic = iconv_open(strcat(t, "//IGNORE"), from_cp);

"//IGNORE" - это поддерживает GNU iconv, но в POSIX этого нет. Осторожно =).

>> char* out = (char*)calloc(strlen(s), sizeof(char));

AFAIK sizeof(char) == 1 всегда, так что смысл? Плюс ко всему iconv может отработать исходную строку не за один вызов, так что он сдвигает out вперёд. Так что:

char *out_buf = out;

>> return out;

out_buf[k] = '\0'; /* iconv не делает это сам */ return out_buf; /* читай выше */

Думаю чтобы просто *заработало* этого будет достаточно. А вообще надо учитывать случаи, когда сконвертированная строка будет длиннее исходной и придётся вызывать iconv несколько раз.

Deleted
()
Ответ на: комментарий от Deleted

Вот работающий пример (но возможно я где-то напортачил =)):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <iconv.h>

#define BUF_SZ 512

static char *convertCharset(char *str, const char *from, const char *to)
{
        char    *buf = NULL, *out;
        size_t   buf_sz, out_left, in_left;
        iconv_t  conv = (iconv_t) -1;
        size_t   converted;

        in_left = strlen(str);

        /* Initial buffer for conversion. */
        buf = malloc(BUF_SZ);
        if (!buf)
                goto on_error;
        buf_sz = BUF_SZ;
        out = buf;
        out_left = BUF_SZ - 1 /* '\0' */;

        /* Create iconv descriptor. */
        conv = iconv_open(to, from);
        if (((iconv_t) -1) == conv)
                goto on_error;

        while (1)
        {
                converted = iconv(conv, &str, &in_left, &out, &out_left);
                if (((size_t) -1) == converted)
                {
                        if (E2BIG == errno)
                        {
                                /* We need more space. */
                                char *new_buf;

                                buf_sz += BUF_SZ;
                                new_buf = realloc(buf, buf_sz);
                                if (!new_buf)
                                        goto on_error;
                                out = new_buf + (out - buf);
                                buf = new_buf;
                                out_left += BUF_SZ;
                        } else {
                                goto on_error;
                        }
                }

                /* All done. */
                if (!in_left)
                {
                        buf[buf_sz - out_left - 1] = '\0';
                        break;
                }


        }

        iconv_close(conv);

        return buf;
on_error:
        free(buf);
        if (((iconv_t) -1) != conv)
                iconv_close(conv);

        return NULL;
}

int main()
{
        char *str = convertCharset("привет!!!", "utf-8", "cp1251");

        if (str)
                printf("Result: %s\n", str);
        else
                fputs("ERROR!\n", stderr);

        free(str);

        return 0;
}

Deleted
()

#include <stdio.h>

/* gcc -DHAVE_ICONV -o ../bin/badpdf badpdf.c */

#ifdef HAVE_ICONV
#include <iconv.h>

iconv_t cd;
char in[2] = {0, 0};
char out[16];
char *inbuf;
char *outbuf;
size_t inbytesleft;
size_t outbytesleft;

#endif

/* See as cp1251 */
const unsigned char cp1251[256] = {
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
	0xa0, 'А',  'Б',  'В' , 'Г',  'Д',  'Е',  'Ё',  'Ж',  'З',  'И',  'Й',  'К',  'Л',  'М',  'Н',
	'О',  'П',  'Р',  'С',  'Т',  'У',  'Ф',  'Х',  'Ц',  'Ч',  'Ш',  'Щ',  'Ъ',  'Ы',  'Ь',  'Э',
	'Ю',  'Я',  'а',  'б',  'в' , 'г',  'д',  'е',  'ё',  'ж',  'з',  'и',  'й',  'к',  'л',  'м',
	'н',  'о',  'п',  'р',  'с',  'т',  'у',  'ф',  'х',  'ц',  'ч',  'ш',  'щ',  'ъ',  'ы',  'ь',
	'э',  'ю',  'я',  0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};

const unsigned char koi8r[256] = {
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
	0xa0, 'А',  'Б',  'В' , 'Г',  'Д',  'Е',  'Ё',  'Ж',  'З',  'И',  'Й',  'К',  'Л',  'М',  'Р',
	'О',  'П',  'Р',  'С',  'Т',  'У',  'Ф',  'Х',  'Ц',  'Ч',  'Ш',  'Щ',  'ё',  'Ы',  'Ь',  'Э',
	'А',  'Б',  'В',  'б',  'Д' , 'Е',  'д',  'З',  'И',  'ж',  'з',  'Л',  'М',  'н',  'О',  'П',
	'Н',  'С',  'Т',  'р',  'с',  'т',  'у',  'Ч',  'х',  'ц',  'ч',  'ш',  'щ',  'Э',  'ы',  'ь',
	'а',  'б',  'в',  0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};

unsigned int c;

int main()
{
#ifdef HAVE_ICONV
	cd = iconv_open("utf-8", "cp1251");
	/* (iconv_t)(-1) if error */
#endif
	c = getc(stdin);
	while (c != EOF)
	{
#ifdef HAVE_ICONV
		in[0] = (char) cp1251[c];
		inbytesleft  = 2;
		outbytesleft = 16;
		inbuf = &in;
		outbuf = &out;
		iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
		printf("%s", out);
#else
		printf("%c", cp1251[c]);
#endif
		c = getc(stdin);
	}

#ifdef HAVE_ICONV
	iconv_close(cd);
#endif
	return 0;
}

ip1981 ☆☆
()
Ответ на: комментарий от Deleted

ЭТО работает.

ЭТО для "расшифровки" PDF сделанных с помощью pdfLaTeX из LaTeX в кодировке cp1251 без пакета cmap.

Выводит в кодировке UTF-8, если с iconv, иначе в cp1251.

ip1981 ☆☆
()

Какие версии GLIBC и iconv ?

Там еще надо возможно баг учитывать при выделении памяти для результата преобразования (размер кратный 4 байтам).

ef37 ★★
()
Ответ на: комментарий от ip1981

>> ЭТО работает.

На системе с UTF-8 оно даже не скомпилируется, если специально не указать в какой кодировке исходник =). Кстати в какой?

>> Выводит в кодировке UTF-8, если с iconv, иначе в cp1251.

В чём смысл выводить в разных жёстко заданных кодировках? Тем более что GNU iconv есть даже на венде...

Deleted
()

char* t = (char*)malloc(strlen(to_cp));
                        ^^^^^^^^^^^^^
              для строки выделяй strlen(to_cp)+1

anonymous
()
Ответ на: комментарий от Deleted

> в какой кодировке исходник =). Кстати в какой?

> /* See as cp1251 */

> В чём смысл выводить в разных жёстко заданных кодировках? Тем более что GNU iconv есть даже на венде...

Смысл --- освоить iconv, встроив его в программу, а не в конвеер.

ip1981 ☆☆
()

Спасибо всем большое. Разобрался. Оказывается функция iconv двигает указатель на out буфер, поэтому нужно сохранить указатель на начало этого буфера.

Barlog_M
() автор топика

Нормальненько так. Одна ф-ция, которая не работает, три утечки памяти и один выход за границы массива. Правильной дорогой идешь ;)

php-coder ★★★★★
()
Ответ на: комментарий от php-coder

Сейчас это выглядит так, сколько тут чего не работает и сколько утечек памяти? При попытке free(in) все падает.

char* convert(const char* s, const char* from_cp, const char* to_cp) { iconv_t ic = iconv_open(to_cp, from_cp);

if (ic == (iconv_t)(-1)) { fprintf(stderr, "iconv: cannot convert from %s to %s\n", from_cp, to_cp); return ""; }

char* out_buf = (char*)calloc(strlen(s)+1, 1); char* out = out_buf; char* in = (char*)malloc(strlen(s)+1); strcpy(in, s);

size_t in_ln = strlen(s); size_t out_ln = in_ln; size_t k = iconv(ic, &in, &in_ln, &out, &out_ln); if(k == (size_t)-1) fprintf(stderr, "iconv: %u of %d converted\n", k, strlen(s));

if(errno != 0) fprintf(stderr, "iconv: %s\n", strerror(errno));

iconv_close(ic);

return out_buf; }

int main() { char* s = convert("Тестовая строка", "utf-8", "cp1251"); printf("CP1251: %s\n", s); free(s); return EXIT_SUCCESS; }

Barlog_M
() автор топика
Ответ на: комментарий от Barlog_M

Чортовы параграфы текса...

Сейчас это выглядит так, сколько тут чего не работает и сколько утечек памяти?
Я не понимаю, что там делает iconv (какого хера он вообще меняет входные параметры?), но при попытке free(in) все падает

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

char* convert(const char* s, const char* from_cp, const char* to_cp)
{
	iconv_t ic = iconv_open(to_cp, from_cp);

	if (ic == (iconv_t)(-1)) {
		fprintf(stderr, "iconv: cannot convert from %s to %s\n", from_cp, to_cp);
		return "";
	}

	char* out_buf = (char*)calloc(strlen(s)+1, 1);
	char* out = out_buf;
	char* in = (char*)malloc(strlen(s)+1);
	strcpy(in, s);

	size_t in_ln = strlen(s);
	size_t out_ln = in_ln;
	size_t k = iconv(ic, &in, &in_ln, &out, &out_ln);
	if(k == (size_t)-1)
		fprintf(stderr, "iconv: %u of %d converted\n", k, strlen(s));

	if(errno != 0)
		fprintf(stderr, "iconv: %s\n", strerror(errno));

	iconv_close(ic);

	return out_buf;
}

int main()
{
	char* s = convert("Тестовая строка", "utf-8", "cp1251");
	printf("CP1251: %s\n", s);
	free(s);
	return EXIT_SUCCESS;
}

Barlog_M
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.