如何在C++多平台中将ISO-8859-1的(char *)转换为UTF-8?

5
我正在更改一个使用ISO Latin 1格式处理文本并将数据存储在SQLite数据库中的C++软件。问题是SQLite使用UTF-8……而使用同一数据库的Java模块也使用UTF-8。
我希望有一种方法可以在将字符存储到数据库之前将ISO Latin 1字符转换为UTF-8字符。我需要它在Windows和Mac上工作。
我听说ICU可以做到这一点,但我认为它太臃肿了。我只需要一个简单的转换系统(最好是双向)来处理这两个字符集。
我该如何做到这一点?

2
你使用的是Windows Latin-1还是真正的ISO Latin 1? - user195488
我本来会建议使用Glib的iconv封装器,它可以在任意两种字符集之间轻松转换,但是如果您确信只需要latin1->utf8,那么下面@Evan提供的解决方案是最简单的。不管怎样,ICU对于这个问题来说似乎太庞大了。 - davka
4个回答

17

ISO-8859-1被纳入ISO / IEC 10646和Unicode的前256个代码点。因此,转换非常简单。

对于每个字符:

uint8_t ch = code_point; /* assume that code points above 0xff are impossible since latin-1 is 8-bit */

if(ch < 0x80) {
    append(ch);
} else {
    append(0xc0 | (ch & 0xc0) >> 6); /* first byte, simplified since our range is only 8-bits */
    append(0x80 | (ch & 0x3f));
}

参见http://en.wikipedia.org/wiki/UTF-8#Description 获取更多细节。

编辑:根据ninjalj的评论,latin-1直接转换为前256个Unicode编码点,因此上述算法应该可行。


2
就像我之前所说的一样,如果它是真正的Latin1编码。Windows CP1252(有时被错误地称为Latin1)有额外的字符(在ISO-8859中保留用于控制字符的范围内),最显著的是开头和结尾引号的版本。 - ninjalj
2
哦,SO上没有下面的东西;-P - ninjalj
2
(ch & 0xc0) >> 6 是多余的。你可以直接写成 ch >> 6 - dan04
@dan04:明确无误从来不会有害。 - Evan Teran
我真的无法理解维基百科链接中的表格。所以,如果我有Latin-1 Ç,那么它属于下面的11位之一,但上述公式是如何工作的呢? - spakai
这个演示将为您带来一些启发 - http://www.codeguru.com/cpp/misc/misc/multi-lingualsupport/article.php/c10451/The-Basics-of-UTF8.htm - spakai

2

我使用以下代码进行C++编程:

std::string iso_8859_1_to_utf8(std::string &str)
{
    string strOut;
    for (std::string::iterator it = str.begin(); it != str.end(); ++it)
    {
        uint8_t ch = *it;
        if (ch < 0x80) {
            strOut.push_back(ch);
        }
        else {
            strOut.push_back(0xc0 | ch >> 6);
            strOut.push_back(0x80 | (ch & 0x3f));
        }
    }
    return strOut;
}

这个解决方案在 Unix 系统上似乎对我有效,但在 Windows 和 Visual Studio 上似乎无效。有人有什么想法吗? - MaestroMaus

1
如果通用字符集框架(如iconv)对您来说太臃肿了,那么请自己编写。

组成一个静态转换表格(字符到UTF-8序列),拼凑出自己的转换。根据您用于字符串存储的方式(char缓冲区、std::string等等),看起来可能会有所不同,但是其基本思想是-遍历源字符串,将每个编码超过127的字符替换为其UTF-8对应字符串。由于这可能会增加字符串长度,因此进行就地操作会相当不方便。为了获得额外的好处,您可以分两步完成:第一步确定目标字符串大小,第二步执行转换。


2
如果它是真正的Latin1编码,那么翻译表就很简单了,Latin1直接映射到前256个Unicode代码点。 - ninjalj
@ninjalj,这个答案并不是建议将其转换为代码点,而是转换为UTF-8序列。每个序列将是一个或两个字节。 - Mark Ransom
@Mark Ransom:这是一样的,生成表格而无需查看大量字符表非常简单。 - ninjalj
@Mark:顺便提一下,你需要将其从/到CP1252进行翻译。 - ninjalj

0

如果您不介意多做一份拷贝,您可以将您的ISO Latin 1字符“扩展”为16位字符,从而获得UTF-16。然后,您可以使用类似UTF8-CPP的工具将其转换为UTF-8。

事实上,我认为UTF8-CPP甚至可以直接将ISO Latin 1转换为UTF-8(utf16to8函数),但您可能会收到警告。

当然,它需要是真正的ISO Latin 1,而不是Windows CP 1232。


两个翻译而不是一个? - Seva Alekseyev
一个不是翻译 - ISO Latin 1的代码单元与UTF16的代码单元完全相同,只是大小不同。这就是为什么我说他可能可以直接将Latin1字符串提供给utf16to8函数的原因。 - Nemanja Trifunovic
我尝试了这个解决方案。可惜它在特殊字符(如ë)上失败了。不过理论很好。 - MaestroMaus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接