c - 有没有办法将 UTF8 转换为 ISO-8859-1?

标签 c linux utf-8 character-encoding embedded

我的软件正在获取 UTF8 中的一些字符串,而我需要将其转换为 ISO 8859 1. 我知道 UTF8 域比 ISO 8859 大。但是 UTF8 中的数据之前已经从 ISO 向上转换,所以我应该不会错过任何东西.

我想知道是否有一种简单/直接的方法可以将 UTF8 转换为 iso-8859-1。

最佳答案

这是一个您可能会觉得有用的函数:utf8_to_latin9()。它转换为 ISO-8859-15(包括 ISO-8859-1 没有的欧元),但也适用于 UTF-8->ISO-8859-1 转换部分 ISO-8859-1->UTF-8-> ISO-8859-1 往返。

该函数忽略无效代码点,类似于 iconv 的 //IGNORE 标志,但不会重组分解的 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。我故意这样写的,因为它应该在嵌入式环境中为您节省一些精力,尽管这种方法有点受限。定制和扩展。

编辑:

我包含了一对转换函数 in an edit to this answer对于 Latin-1/9 到/从 UTF-8 的转换(ISO-8859-1 或 -15 到/从 UTF-8);主要区别在于这些函数返回一个动态分配的副本,并保持原始字符串不变。

关于c - 有没有办法将 UTF8 转换为 ISO-8859-1?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11156473/

相关文章:

c++ - 将双端队列与 C 库一起使用

linux - 为什么 Ubuntu 终端在运行负载测试时会关闭?

linux - WMI 在 LINUX 中设置变量?

python - Elementtree 和 Unicode 或 UTF-8 混淆

c - 为什么不是垃圾值

c - fprintf valgrind 错误

c++ - 如何在不同机器上保持固定大小的 C 变量类型?

linux - 每 10 分钟配置一次 Cron 任务并在 6 :30 am 开始

string - Python3 将非英文字符转换为英文字符

php - WordPress 安装错误 [未知字符集 : 'utf' ]