【问题标题】:Convert ISO-8859-1 strings to UTF-8 in C/C++在 C/C++ 中将 ISO-8859-1 字符串转换为 UTF-8
【发布时间】:2010-10-30 17:17:37
【问题描述】:

您可能认为这很容易获得,但我很难找到一个简单的库函数,它将 C 或 C++ 字符串从 ISO-8859-1 编码转换为 UTF-8。我正在读取采用 8 位 ISO-8859-1 编码的数据,但需要将其转换为 UTF-8 字符串以用于 SQLite 数据库并最终用于 Android 应用程序。

我找到了一款商业产品,但目前超出了我的预算。

【问题讨论】:

  • 没有什么简单的。您可以使用开源 ICU 库。
  • 如果必须这样做,那么最简单的代码是预先生成一个表,其中包含 128 个(左右)UTF-8 字符对应于设置了最高位的 8859-1 个字符。其他 128 个 8859-1 字符未修改。这样,您的代码根本不必理解 Unicode。另外,请注意 ISO-8859-1 和 Windows CP-1252 之间的区别。后者有一些额外的字符,其中 8859-1 有间隙(未使用的代码点)。除非您应该验证您的输入确实是 ISO-8859-1,否则不接受 CP-1252 是没有意义的,因为您看到它被贴错标签。
  • @Steve:由于 UTF-8 是可变长度的(在这种情况下,每个字符 1 或 2 个字节),查找表并不那么容易使用。请参阅我的答案,它应该同样快速且简单得多。
  • @R.:嗯,“简单”是一个相对术语。 stpcpy 有帮助,前提是你是那种擅长缓冲区大小的程序员。
  • stpcpy(即使它是标准的或现在正朝着标准的方向发展..?)对于 1 字节和 2 字节副本来说是一个巨大的开销。如果复制的字节为 0(几乎可以肯定是无分支的),你最好总是复制 2 个字节(手动)并包含一些代码来跳过第二个指针前进。

标签: c++ c


【解决方案1】:

如果您的源编码将始终为 ISO-8859-1,这很简单。这是一个循环:

unsigned char *in, *out;
while (*in)
    if (*in<128) *out++=*in++;
    else *out++=0xc2+(*in>0xbf), *out++=(*in++&0x3f)+0x80;

为了安全起见,您需要确保输出缓冲区是输入缓冲区的两倍,或者包含大小限制并在循环条件中检查。

【讨论】:

  • 哇。这很有帮助!我并不期待另一种表查找算法。现在是 ANSEL 到 UTF-8...
  • 这肯定回答了这个问题。但正如我在上面的评论中所说,人们向您发送错误标记为 ISO-8859-1 的 CP-1252。 Web 服务器是我遇到的一个例子,它说服了我这个问题,但也有一些文本编辑器声称它们保存为“Latin-1”,但实际上它们不是。 “如果您的源编码将始终是 ISO-8859-1”是一个相当大的“如果”,可能很难追查并消除肇事者。
  • @gordon:我不熟悉 ANSEL,但您应该知道 ISO-8859-1 是 唯一 很容易转换为 UTF- 的传统编码8.其他一切都需要查找表。一个史蒂夫说,我的“如果..”是一个如果。
  • 从可维护性的角度来看,这是一个写得很糟糕的代码。使用更多的大括号。
  • @Nick:是的,我的意思是 0xA0,只是在我的脑海中错误地转换为十进制。评论太旧了,无法编辑。 ?
【解决方案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;
}

【讨论】:

  • 能分享一下 Latin7 的版本吗?
【解决方案3】:

您可以使用 boost::locale 库:

http://www.boost.org/doc/libs/1_49_0/libs/locale/doc/html/charset_handling.html

代码如下所示:

#include <boost/locale.hpp>
std::string utf8_string = boost::locale::conv::to_utf<char>(latin1_string,"Latin1");

【讨论】:

    【解决方案4】:

    C++03 标准不提供直接在特定字符集之间转换的函数。

    根据您的操作系统,您可以在 Linux 上使用 iconv(),在 Windows 上使用 MultiByteToWideChar() & Co.。 为字符串转换提供大量支持的库是 ICU 库,它是开源的。

    【讨论】:

    • > C++ 标准没有提供直接在字符集之间转换的函数
    【解决方案5】:

    Unicode 人员有一些表格,如果面对 Windows 1252 而不是真正的 ISO-8859-1,可能会有所帮助。最终的似乎是this one,它将 CP1252 中的每个代码点映射到 Unicode 中的代码点。将 Unicode 编码为 UTF-8 是一项简单的练习。

    在编译时直接解析该表并从中形成查找表并不难。

    【讨论】:

      【解决方案6】:

      ISO-8859-1 到 UTF-8 仅涉及编码算法,因为 ISO-8859-1 是 Unicode 的子集。所以你已经有了 Unicode 代码点。查看 Wikipedia 中的算法。

      C++ 方面——将其与 iostream 集成——要困难得多。

      我建议你绕着那座山走,而不是试图钻过它或爬上它,即实现一个简单的字符串到字符串的转换器。

      干杯,

      【讨论】:

      • 该算法并不完全是微不足道的,尤其是当新手到中级 C 编码人员经常错误地使用 char * 而需要 unsigned char * 时。 UTF-8 的定义中有更重要的重要意义,特别是您需要拒绝代理代码点和超出范围的值。值得庆幸的是,这些不会出现在只需要处理 ISO-8859-1 输入的编码器中,但是如果你编写了这样一个有限的编码器,那么以后很可能有人会在没有添加任何检查的情况下误用它以获得更大的输入范围。
      • @MichałLeon:Unicode 不是一种编码。 Unicode 有许多不同的编码,包括 UTF-8 和 UTF-16。 Unicode 的前 256 个代码点与拉丁语 1(又名 ISO-8859-1)相同。注意:强调不会让你减少与琐碎事实的矛盾。下一次,不要大喊大叫和投反对票,而是考虑简单地检查事实,或者只是问任何你不明白的事情。
      • @Martin:Unicode 代码点 128 到 255 的块称为 "Latin-1 supplement" of Unicode,因为它与 Latin-1 相同。 Unicode 是 Latin-1 的直接扩展。你们这些cmets是荒谬的废话,那种可以影响非技术人员并表示拖钓的技术喋喋不休。我猜你是在拖钓。
      • @MichałLeon:好的,抱歉。我也许应该猜到了:多年来,我帮助了一个视力极差的学生,而她经常看不到那里有什么。在 OP 的帖子、我的答案、我的所有 cmets 以及除一个以外的其他答案中都指定了 Latin-1。
      【解决方案7】:

      为什么你需要 -1 而不是 -7。根据我对sql的测试,你甚至不能在-1中存储特殊字符。那么你到底想转换什么?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-27
        • 2014-07-04
        • 2020-01-25
        • 2014-08-29
        • 2019-02-21
        • 2016-07-29
        • 2012-07-06
        相关资源
        最近更新 更多