belchenko
@belchenko

Как заставить iconv работать?

Всеми привет!
Я пишу конвертер из древнего формата Лексера и нашел библиотеку, которая дружит с ним. Она возвращает несколько событий на каждый знак в файле .lex. Вот одно из событий это символ, в оригинале он хранится в кодировке CP866, написал маленькую функцию, которая конвертирует текущий символ в ютф, но половину символов iconv просто игнорирует и не может прочитать :(

Код представляет собой франкинштейна, поэтому не ругайте за это пожалуйста

#include <stdio.h>
#include <stdint.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "liblex.h"
#include <iconv.h>
#include <errno.h>
#include <err.h>

#define ROW_SIZE 512


void parse_callback( struct parser_event *pe, void *userdata )
{
    iconv_t foo = iconv_open("CP866", "UTF-8");
    if((int) foo == -1) {
        if (errno == EINVAL) {
            fprintf(stderr,
                    "Conversion is not supported");
        } else {
            fprintf(stderr, "Initialization failure:\n");
        }
    }
    // calloc fills memory with 0 bytes. we alloc two -
    // one for the 'ö' and one for the ending delimeter
    char *iso = calloc(2, sizeof(char));

    // the converted string can be four times larger
    // then the original, as the largest known char width is 4 bytes.
    char *converted = calloc(5, sizeof(char));

    // we need to store an additional pointer that targets the
    // start of converted. (iconv modifies the original 'converted')
    char *converted_start = converted;

    size_t ibl = 2; // len of iso
    size_t obl = 5; // len of converted

    // do it!

    switch ( pe->event ) {

        case EVENT_RUNE:
            iso[0] = pe->rune;
            int ret = iconv(foo, &iso, &ibl, &converted, &obl);

            // if iconv fails it returns -1
            if(ret == (iconv_t)-1) {
                perror("iconv");
                iconv_close(foo);
            } else {
                // other wise the number of converted bytes
                printf("%i bytes converted\n", ret);
                printf("result: '%s'\n", converted_start);
                iconv_close(foo);
            }
            fprintf( stdout, "debug: event = RUNE, data = %c\n", iso);
            break;

        case EVENT_FONT:
            fprintf( stderr, "FUCK FONT\n");
            fprintf( stdout, "debug: event = FONT, data = %d\n", pe->font_id );
            break;

        case EVENT_ERROR:
            fprintf( stderr, "FUCK ERROR\n");
            fprintf( stdout, "debug: event = ERROR\n" );
            break;

        default:
            fprintf( stdout, "debug: event = UNKNOWN\n" );
    }
}


int main()
{
    int err = 0;
    struct parser *p;
    struct parser_event pe;
    int fd;
    uint8_t row[ROW_SIZE];
    ssize_t size;
    fprintf( stderr, "FUCK IM STARTED BITCH\n");
    /* Create new parser */
    err = parser_create( &p );
    if ( err ) {
        fprintf( stderr, "error: Unable to create parser\n" );
        goto out;
    }

    fprintf( stderr, "FUCK PARSER\n");
    fd = open("example.lex", O_RDONLY);
    if ( fd == -1 ) {
        fprintf( stderr, "error: Unable to open stream\n" );
        goto out;
    }
    fprintf( stderr, "FUCK FILES\n");
    err = parser_set_callback( p, parse_callback, NULL );
    if ( err ) {
        fprintf( stderr, "error: Unable to setup callback\n" );
        goto out;
    }
    fprintf( stderr, "FUCK IT CALLBACK\n");
    for ( ;5; ) {

        /* Read */
        size = read( fd, row, ROW_SIZE );
        if ( size == 0 ) {
            goto out;
        }
        if ( size == -1 ) { /* Some error when reading */
            fprintf( stderr, "error: Unable to read bytes\n" );
            goto out;
        }

        /* Parse */
        err = parser_parse( p, row, size );
        if ( err ) {
            fprintf(stderr, "error: Unable to parsing row\n");
            goto out;
        }

    }

  out:

    /* Close stream */
    if ( fd != -1 ) {
        close( fd );
    }

    /* Dispose reader */
    parser_dispose( p );

    return err;
}
  • Вопрос задан
  • 731 просмотр
Решения вопроса 1
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Как заставить iconv работать?

Я в вашем коде вижу две ошибки и несколько странных мест. Ошибки:
- вы создаёте конвертор на каждый вызов parse_callback, но закрываете его только по событию EVENT_RUNE.
- вы выделяете память (два calloc в parse_callback) и не освобождаете её. При том, что динамическая память вам вообще здесь не нужна.
Странные места:
- printf("result: '%s'\n", converted_start); в converted_start -- utf8...
-
fprintf(stdout, "debug: event = RUNE, data = %c\n", iso);
iso -- это во-первых массив, а во-вторых, массив символов CP866.
Как минимум один (а скорее всего оба) из этих printf не выведет того, что вы ожидали.

parse_callback можно переписать следующим образом:
void parse_callback( struct parser_event *pe, void *userdata )
{
    switch ( pe->event ) {

        case EVENT_RUNE: {
            char *iso = &pe->rune;
            char out[10] = {0};
            char *converted = out;
            size_t ibl = 1;
            size_t obl = sizeof(out);
            iconv_t foo = iconv_open("CP866", "UTF-8");
            int ret;

            if((int) foo == -1) {
                if (errno == EINVAL) {
                    fprintf(stderr,
                            "Conversion is not supported");
                } else {
                    fprintf(stderr, "Initialization failure:\n");
                }
                break;
            }
            ret = iconv(foo, &iso, &ibl, &converted, &obl);

            // if iconv fails it returns -1
            if(ret == (iconv_t)-1) {
                perror("iconv");
            } else {
                // otherwise the number of converted bytes
                printf("%i bytes converted\n", ret);
                printf("result: '%s'\n", out);
            }
            iconv_close(foo);
            fprintf(stdout, "debug: event = RUNE, data = %c\n", pe->rune);
            break;
        }

        case EVENT_FONT:
            fprintf( stderr, "FUCK FONT\n");
            fprintf( stdout, "debug: event = FONT, data = %d\n", pe->font_id );
            break;

        case EVENT_ERROR:
            fprintf( stderr, "FUCK ERROR\n");
            fprintf( stdout, "debug: event = ERROR\n" );
            break;

        default:
            fprintf( stdout, "debug: event = UNKNOWN\n" );
    }
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы