C конвертировать ISO-8859-1 в UTF-8 с помощью libconv

Мне нужно закодировать латинские символы ISO-8859-1 в UTF-8 (и обратную операцию тоже).

Сначала я использовал этот ответ способ конвертировать из UTF8 в iso-8859-1? выполнить операцию и все работает;

Теперь я хочу использовать libiconv, который предоставляет все механизмы преобразования и должен помочь мне упростить мой код.

Я следовал приведенному здесь примеру: https://www.lemoda.net/c/iconv-example/iconv-example.html

и я написал метод, который выглядит так:

char *iconvISO2UTF8(char *iso) {
    iconv_t iconvDesc = iconv_open ("ISO−8859-1", "UTF-8//TRANSLIT//IGNORE");

    if (iconvDesc == (iconv_t) - 1) {
        /* Something went wrong.  */
        if (errno == EINVAL)
            fprintf(stderr, "conversion from '%s' to '%s' not available", "ISO−8859−1", "UTF-8");           
        else
            fprintf(stderr, "LibIcon initialization failure");          

        return NULL;
    }

    size_t iconv_value;
    char * utf8;
    size_t len;
    size_t utf8len; 
    char * utf8start;

    int len_start;


    len = strlen (iso);
    if (! len) {        
        fprintf(stderr, "iconvISO2UTF8: input String is empty.");           
        return NULL;
    }

    /* Assign enough space to put the UTF-8. */
    utf8len = 2 * len;
    utf8 = calloc (utf8len, sizeof (char));
    if (! utf8) {
        fprintf(stderr, "iconvISO2UTF8: Calloc failed.");           
        return NULL;
    }
    /* Keep track of the variables. */
    utf8start = utf8;
    len_start = len;

    iconv_value = iconv (iconvDesc, & iso, & len, & utf8, & utf8len);
    /* Handle failures. */
    if (iconv_value == (size_t) - 1) {      
        switch (errno) {
                /* See "man 3 iconv" for an explanation. */
            case EILSEQ:
                fprintf(stderr, "iconv failed: Invalid multibyte sequence, in string '%s', length %d, out string '%s', length %d\n", iso, (int) len, utf8start, (int) utf8len);             
                break;
            case EINVAL:
                fprintf(stderr, "iconv failed: Incomplete multibyte sequence, in string '%s', length %d, out string '%s', length %d\n", iso, (int) len, utf8start, (int) utf8len);              
                break;
            case E2BIG:
                fprintf(stderr, "iconv failed: No more room, in string '%s', length %d, out string '%s', length %d\n", iso, (int)  len, utf8start, (int) utf8len);                              
                break;
            default:
                fprintf(stderr, "iconv failed, in string '%s', length %d, out string '%s', length %d\n", iso, (int) len, utf8start, (int) utf8len);                             
        }
        return NULL;
    }


    if(iconv_close (iconvDesc) != 0) {
        fprintf(stderr, "libicon close failed: %s", strerror (errno));          
    }

    return utf8start;

}

Когда я вызываю эту функцию с помощью простых старых символов, подобных ascii, таких как «абракадабра», работает iconv. Но как только я отправляю ему акцентированные символы, например "éàèüöä", вызов iconv() завершается с ошибкой с кодом EILSEQ:

iconv не удалось: недопустимая многобайтовая последовательность, в строке 'éàèüöä', длина 6, исходящая строка '', длина 12

Вот пример основной программы, которая дает сбой при сохранении в исходном файле, закодированном с помощью ISO-8859-1 и скомпилированном в системе Linux с ISO-8859-1 в качестве набора символов по умолчанию:

int main(int argc, char **argv) {
    char *iso1 = "abracadabra";
    char *utf = iconvISO2UTF8(iso1);
    puts(utf);
    free(utf);

    char *iso2 = "éàèüöä";
    utf = iconvISO2UTF8(iso2);
    puts(utf);
    free(utf);
}

Можно ли запустить такое преобразование с помощью iconv? Если да, что не так в этом коде?


person Guillaume    schedule 17.09.2018    source источник
comment
Что такое iso, т.е. откуда оно взялось? Вы уверены, что это в правильной кодировке? В строке Latin-1 не должно быть многобайтовых символов.   -  person unwind    schedule 17.09.2018
comment
iso — это char*, который исходит от программы (сама закодирована в ISO-8859-1) и должен быть преобразован в utf-8 для использования в другой lib (jansson), которая обрабатывает JSON. Я отредактировал свой вопрос.   -  person Guillaume    schedule 17.09.2018
comment
Набор символов исходного компилятора, который представляет собой кодировку, в которой сохранен исходный файл, не обязательно совпадает с набором символов execution, а строковые литералы набора символов не закодированы. in. Если вы хотите закодировать строковую константу в формате ISO-8859-1, вы можете использовать escape-последовательности, такие как "\xe2\xe9xef\xf8\xf9\xfd". Это будет работать в любой исходной кодировке (а UTF-8 — единственная кодировка, которую поддерживают некоторые компиляторы, включая clang). Вы также можете #define EACUTE "\xe9", а затем воспользоваться преимуществами препроцессора, чтобы написать "fianc" EACUTE.   -  person Davislor    schedule 17.09.2018
comment
В C11 вы также можете написать u8"Üñìçõðæ", чтобы получить UTF-8. Если ваша исходная кодировка не поддерживает данную кодовую точку Unicode, вы можете использовать escape-коды \u или \U в любой исходной кодировке.   -  person Davislor    schedule 17.09.2018
comment
Я настоятельно рекомендую сохранять все исходные файлы C как UTF-8 с отметкой порядка байтов. (Некоторые версии MSVC не могут скомпилировать UTF-8 без BOM, а некоторые другие компиляторы не могут скомпилировать ничего, кроме UTF-8 или ASCII, так что это единственное, что работает на каждом компиляторе, который мне нужно использовать.) Сохранение в другой encoding не является безопасным, переносимым и надежным способом указания кодировки строковых литералов, но он нарушит вашу программу в некоторых цепочках инструментов. Например, когда вы разместили здесь, ваш фрагмент был преобразован в UTF-8.   -  person Davislor    schedule 17.09.2018
comment
Примечание: C не поддерживает методы.   -  person too honest for this site    schedule 17.09.2018
comment
softwareengineering.stackexchange.com/questions/20909/ @Davislor, конечно, это лучшая практика; однако я работаю с огромной базой устаревшего кода, поэтому этой рискованной миграции (жестко запрограммированная длина буфера повсюду) не произойдет.   -  person Guillaume    schedule 17.09.2018
comment
@Guillaume Это имеет смысл. Прагматизм против догматизма! Если код будет молча ломаться, когда исходный набор выполнения не является Latin-1, по крайней мере, рассмотрите возможность написания ваших строк с переносимыми и очевидными экранами \x. Ваш проект подвержен высокому риску загнивания.   -  person Davislor    schedule 17.09.2018


Ответы (1)


Пожалуйста, внимательно прочитайте страницу руководства iconv_open(3):

iconv_t iconv_open(const char *tocode, const char *fromcode);

Если вы конвертируете в UTF-8 из ISO 8859-1, то это противоречит:

iconv_t iconvDesc = iconv_open ("ISO−8859-1", "UTF-8//TRANSLIT//IGNORE");

Он должен сказать

iconv_t iconvDesc = iconv_open ("UTF-8//TRANSLIT//IGNORE", "ISO−8859-1");
person Antti Haapala    schedule 17.09.2018
comment
Кхм, это явно был случай PBCK... В моем представлении это было convert(from, to), и после этого я не обращал внимания на порядок аргументов! - person Guillaume; 17.09.2018