Вопрос по linux, character-encoding, embedded, c, utf-8 – Есть ли способ конвертировать из UTF8 в iso-8859-1?

8

Мое программное обеспечение получает несколько строк в UTF8, чем мне нужно преобразовать в ISO 8859 1. Я знаю, что домен UTF8 больше, чем ISO 8859. Но данные в UTF8 ранее были преобразованы с повышением частоты из ISO, поэтому я не должен ничего пропустить.

Я хотел бы знать, есть ли простой / прямой способ конвертировать из UTF8 в ISO-8859-1.

Спасибо

Если вы используете библиотеку, которая выполнила преобразование, у нее также должно быть что-то, чтобы преобразовать ее обратно. Предполагая, что вы не изменили никаких символов в строке, все будет в порядке, просто вернув его. RedX

Ваш Ответ

2   ответа
12

utf8_to_latin9(), Конвертируется вISO-8859-15 (включая евро, которыйISO-8859-1 не имеет), но также работает правильно дляUTF-8- & GT;ISO-8859-1 преобразование частьISO-8859-1- & GT;UTF-8- & GT;ISO-8859-1 поездка туда и обратно.

Функция игнорирует недопустимые кодовые точки, похожие на//IGNORE флаг для iconv, но не перекомпоновывает разложенные последовательности UTF-8; то есть он не повернетU+006E U+0303 вU+00F1, Я не беспокоюсь о перекомпоновке, потому что iconv тоже ничего не делает.

Функция очень осторожна с доступом к строке. Он никогда не будет сканировать за пределы буфера. Выходной буфер должен быть на один байт длиннее, потому что он всегда добавляет NUL-конец конца строки. Функция возвращает количество символов (байтов) в выводе, не считая байта NUL конца строки.

/* UTF-8 to ISO-8859-1/ISO-8859-15 mapper.
 * Return 0..255 for valid ISO-8859-15 code points, 256 otherwise.
*/
static inline unsigned int to_latin9(const unsigned int code)
{
    /* Code points 0 to U+00FF are the same in both. */
    if (code < 256U)
        return code;
    switch (code) {
    case 0x0152U: return 188U; /* U+0152 = 0xBC: OE ligature */
    case 0x0153U: return 189U; /* U+0153 = 0xBD: oe ligature */
    case 0x0160U: return 166U; /* U+0160 = 0xA6: S with caron */
    case 0x0161U: return 168U; /* U+0161 = 0xA8: s with caron */
    case 0x0178U: return 190U; /* U+0178 = 0xBE: Y with diaresis */
    case 0x017DU: return 180U; /* U+017D = 0xB4: Z with caron */
    case 0x017EU: return 184U; /* U+017E = 0xB8: z with caron */
    case 0x20ACU: return 164U; /* U+20AC = 0xA4: Euro */
    default:      return 256U;
    }
}

/* Convert an UTF-8 string to ISO-8859-15.
 * All invalid sequences are ignored.
 * Note: output == input is allowed,
 * but   input < output < input + length
 * is not.
 * Output has to have room for (length+1) chars, including the trailing NUL byte.
*/
size_t utf8_to_latin9(char *const output, const char *const input, const size_t length)
{
    unsigned char             *out = (unsigned char *)output;
    const unsigned char       *in  = (const unsigned char *)input;
    const unsigned char *const end = (const unsigned char *)input + length;
    unsigned int               c;

    while (in < end)
        if (*in < 128)
            *(out++) = *(in++); /* Valid codepoint */
        else
        if (*in < 192)
            in++;               /* 10000000 .. 10111111 are invalid */
        else
        if (*in < 224) {        /* 110xxxxx 10xxxxxx */
            if (in + 1 >= end)
                break;
            if ((in[1] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x1FU)) << 6U)
                             |  ((unsigned int)(in[1] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 2;

        } else
        if (*in < 240) {        /* 1110xxxx 10xxxxxx 10xxxxxx */
            if (in + 2 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x0FU)) << 12U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[2] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 3;

        } else
        if (*in < 248) {        /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
            if (in + 3 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x07U)) << 18U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[3] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 4;

        } else
        if (*in < 252) {        /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
            if (in + 4 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U &&
                (in[4] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x03U)) << 24U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 18U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[3] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[4] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 5;

        } else
        if (*in < 254) {        /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
            if (in + 5 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U &&
                (in[4] & 192U) == 128U &&
                (in[5] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x01U)) << 30U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 24U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 18U)
                             | (((unsigned int)(in[3] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[4] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[5] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 6;

        } else
            in++;               /* 11111110 and 11111111 are invalid */

    /* Terminate the output string. */
    *out = '\0';

    return (size_t)(out - (unsigned char *)output);
}

Обратите внимание, что вы можете добавить пользовательскую транслитерацию для определенных кодовых точек вto_latin9() функция, но вы ограничены заменой одного символа.

Как написано в настоящее время, функция может безопасно выполнять преобразование на месте: указатели ввода и вывода могут быть одинаковыми. Выходная строка никогда не будет длиннее входной строки. Если в вашей входной строке есть место для дополнительного байта (например, у него есть NUL, заканчивающий строку), вы можете безопасно использовать вышеупомянутую функцию для преобразования ее из UTF-8 в ISO-8859-1 / 15. Я специально написал это так, потому что это должно сэкономить вам некоторые усилия во встроенной среде, хотя этот подход немного ограничен по сравнению с этим. настройка и расширение.

Редактировать:

Я включил пару функций преобразованияв редактировании к этому ответу для преобразования Latin-1/9 в / из UTF-8 (ISO-8859-1 или -15 в / из UTF-8); Основное различие заключается в том, что эти функции возвращают динамически размещенную копию и сохраняют исходную строку без изменений.

12

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);

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

tocode является"ISO_8859-1" а такжеfromcode является"UTF-8".

Рабочий пример:

#include <iconv.h>
#include <stdio.h>

int main (void) {
    iconv_t cd = iconv_open("ISO_8859-1", "UTF-8");
    if (cd == (iconv_t) -1) {
        perror("iconv_open failed!");
        return 1;
    }

    char input[] = "Test äöü";
    char *in_buf = &input[0];
    size_t in_left = sizeof(input) - 1;

    char output[32];
    char *out_buf = &output[0];
    size_t out_left = sizeof(output) - 1;

    do {
        if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t) -1) {
            perror("iconv failed!");
            return 1;
        }
    } while (in_left > 0 && out_left > 0);
    *out_buf = 0;

    iconv_close(cd);

    printf("%s -> %s\n", input, output);
    return 0;
}
Спасибо, основная проблема у меня есть, и я забыл указать, что мое программное обеспечение работает на встроенном Linux, а iconv недоступен. fazineroso
Вы можете скомпилировать iconv для вашего Linux. Использует ли ваш Linux Glibc? Если да, то его совместимая реализация называетсяgconv: gnu.org/software/libc/manual/html_node/…
Спасибо, что поделились этим. Вам следует позвонитьiconv_close() за освобождение ресурсов, выделенныхiconv_open(), Просто положиiconv_close(cd); передprintf("%s -> %s\n", input, output); линия.
@silvioprog, спасибо за ваш комментарий! Я добавил строку.

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