【问题标题】:How to convert a String from UTF8 to Latin1 in C/C++?如何在 C/C++ 中将字符串从 UTF8 转换为 Latin1?
【发布时间】:2012-10-03 01:36:27
【问题描述】:

我的问题很简单,但到目前为止我找不到解决方案:

如何在不使用任何额外库(如 libiconv)的情况下,在 C++ 中将 UTF8 编码的 string 转换为 latin1 编码的 string

到目前为止,我能找到的每个示例都是针对 latin1 到 UTF8 的转换?

【问题讨论】:

  • UTF8 可以表示 65536 个码位; latin1 (ISO-8859-1) 只能表示256。你想如何处理所有无法转换的字符?
  • @DavidRF 条件“不使用任何额外的库”意味着不使用现成的函数,如给定代码的最后一行 utf8_text.encode('ISO-8859-1', 'replace')
  • @Dialectus 是的,但翻译不是复制粘贴 :) 您可以省略此行以便仅获取要转换的字符
  • 完全停止使用 Latin1 怎么样?

标签: c++ c utf-8 latin1 string-conversion


【解决方案1】:
typedef unsigned value_type;

template <typename Iterator>
size_t get_length (Iterator p)
{
    unsigned char c = static_cast<unsigned char> (*p);
    if (c < 0x80) return 1;
    else if (!(c & 0x20)) return 2;
    else if (!(c & 0x10)) return 3;
    else if (!(c & 0x08)) return 4;
    else if (!(c & 0x04)) return 5;
    else return 6;
}

template <typename Iterator>
value_type get_value (Iterator p)
{
    size_t len = get_length (p);

    if (len == 1)
    return *p;

    value_type res = static_cast<unsigned char> (
                                    *p & (0xff >> (len + 1)))
                                     << ((len - 1) * 6);

    for (--len; len; --len)
        res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6);

    return res;
}

此函数将返回 p 处的 unicode 代码点。您现在可以使用

转换字符串
for (std::string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p)
{
     value_type value = get_value<std::string::iterator&>(p));
     if (value > 0xff)
         throw "AAAAAH!";
     s_latin1.append(static_cast<char>(value));
}

不保证,代码已经很老了:)

【讨论】:

  • 是的,我知道 UTF-8 官方只支持最大 4 字节长度,这可以实现得更迂腐。
  • 这是否也能正确转换德语变音符号(ö,ä,ü,ß)?
  • @ashiaka:我对此表示怀疑......我认为这些字符在 latin1 中不可用......
  • 你认为 latin1 应该编码什么?例如看这里:decodeunicode.org/de/u+00dc。代码点低于 0xff 的所有内容都将被正确转换,高于此的所有内容都将导致异常。确实它也被转换了,但它不能被编码为 8 位。
  • 嗯,这意味着“是” - 谢谢 :)
【解决方案2】:

这是我为我的目的而写的电影人回答的一个版本。更具可读性,可能会慢一些。我不需要模板的东西,因为我一直在处理char *,就我而言,我想用_替换非拉丁语字符。以防万一它对某人有所帮助:

int GetUtf8CharacterLength( unsigned char utf8Char )
{
    if ( utf8Char < 0x80 ) return 1;
    else if ( ( utf8Char & 0x20 ) == 0 ) return 2;
    else if ( ( utf8Char & 0x10 ) == 0 ) return 3;
    else if ( ( utf8Char & 0x08 ) == 0 ) return 4;
    else if ( ( utf8Char & 0x04 ) == 0 ) return 5;

    return 6;
}

char Utf8ToLatin1Character( char *s, int *readIndex )
{
    int len = GetUtf8CharacterLength( static_cast<unsigned char>( s[ *readIndex ] ) );
    if ( len == 1 )
    {
        char c = s[ *readIndex ];
        (*readIndex)++;

        return c;
    }

    unsigned int v = ( s[ *readIndex ] & ( 0xff >> ( len + 1 ) ) ) << ( ( len - 1 ) * 6 );
    (*readIndex)++;
    for ( len-- ; len > 0 ; len-- )
    {
        v |= ( static_cast<unsigned char>( s[ *readIndex ] ) - 0x80 ) << ( ( len - 1 ) * 6 );
        (*readIndex)++;
    }

    return ( v > 0xff ) ? 0 : (char)v;
}

// overwrites s in place
char *Utf8ToLatin1String( char *s )
{
    for ( int readIndex = 0, writeIndex = 0 ; ; writeIndex++ )
    {
        if ( s[ readIndex ] == 0 )
        {
            s[ writeIndex ] = 0;
            break;
        }

        char c = Utf8ToLatin1Character( s, &readIndex );
        if ( c == 0 )
        {
            c = '_';
        }

        s[ writeIndex ] = c;
    }

    return s;
}

测试代码:

char s2[ 256 ] = "lif\xc3\xa9 is b\xc3\xa9tt\xc3\xa9r with acc\xc3\xa9nts";
Utf8ToLatin1String( s2 );

【讨论】:

    【解决方案3】:

    latin1(又名ISO-8859-1)定义了 Unicode 的前 256 个代码点。因此,在UTF-8 中,如果您的字符是 8 位,那么它将精确映射到 latin1 等效项。如果长度超过 8 位,则 latin1 中没有对应的,您应该将其映射到某个“未知字符”(例如,\0 或?)。

    【讨论】:

    • 真的吗?该死的......在这种情况下,我猜OP可以使用它然后手动映射剩余的128个点。
    • 从 UTF-16 到 latin1 的转换只是简单地删除每个偶数零,但是从 UTF-8 到 latin1 的转换有点复杂。
    猜你喜欢
    • 2010-12-04
    • 2015-04-20
    • 1970-01-01
    • 2012-12-09
    • 2011-01-21
    • 2012-03-13
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多