【问题标题】:Read/Store different types of strings (utf8/utf16/ansi)读取/存储不同类型的字符串(utf8/utf16/ansi)
【发布时间】:2013-01-07 10:14:47
【问题描述】:

我正在解析一个文件,其中包含不同编码的各种字符串。这些字符串的存储方式是这样的:

0xFF 0xFF - block header                   2 bytes
0xXX 0xXX - length in bytes                2 bytes
0xXX      - encoding (can be 0, 1, 2, 3)   1 byte
...       - actual string                  num bytes per length

这通常很容易,但是我不确定如何处理编码。编码可以是以下之一:

0x00 - regular ascii string (that is, actual bytes represent char*)
0x01 - utf-16 with BOM (wchar_t* with the first two bytes being 0xFF 0xFE or 0xFE 0xFF)
0x02 - utf-16 without BOM (wchar_t* directly)
0x03 - utf-8 encoded string (char* to utf-8 strings)

我需要以某种方式读取/存储它。最初我想的是简单的string,但这不适用于wchar_t*。然后我考虑将所有内容都转换为wstring,但这将是相当多的不必要的转换。接下来想到的是boost::variant<string, wstring>(我已经在代码的另一个地方使用了boost::variant)。在我看来,这是一个合理的选择。所以现在我有点难以解析它。我正在考虑以下几点:

//after reading the bytes, I have these:
int length;
char encoding;
char* bytes;

boost::variant<string, wstring> value;
switch(encoding) {
    case 0x00:
    case 0x03:
        value = string(bytes, length);
        break;
    case 0x01:
        value = wstring(??);
        //how do I use BOM in creating the wstring?
        break;
    case 0x02:
        value = wstring(bytes, length >> 1);
        break;
    default:
        throw ERROR_INVALID_STRING_ENCODING;
}

因为我只是稍后打印这些字符串,所以我可以将 UTF8 存储在一个简单的 string 中而无需太多麻烦。

我的两个问题是:

  1. 这种方法是否合理(即使用 boost::variant)?

  2. 如何使用特定的 BOM 创建 wstring

【问题讨论】:

  • 看看这里:stackoverflow.com/questions/402283/stdwstring-vs-stdstring(最佳答案),如果你在 Windows 上只有 wstring 是一个可靠的选择,我的意思是整个软件不是 Variant 方法,如果你计划跨平台我建议使用 QT 的文本转换功能(在 QString 中处理所有这些)
  • @Najzero 我是在linux上开发的,但是结果必须能够在windows、linux和mac os x下编译。另外,请注意,我的目标是在任何平台上静态编译的可执行文件少于 300K(超出要求,我不控制这些),因此链接 ICU 或 QT 很可能不是一个选项。

标签: c++ string unicode byte-order-mark wstring


【解决方案1】:

UTF16 需要区分 LE 和 BE。

我怀疑 0x02 - utf-16 without BOM (wchar_t* directly) 实际上是 UTF16 BE。 With BOM 编码意味着 LE/BE 由 BOM 指示。

C++ 标准库的 Unicode 支持非常有限,我认为 vanilla C++ 不能正确处理 UTF16LE/BE,更不用说 UTF8。许多 Unicode 应用程序使用第 3 方支持库,例如 ICU

对于内存中的表示,我会坚持使用 std::string。因为 std::string 可以表示任何文本编码,而 std::wstring 对这种多重编码情况没有多大帮助。如果您需要使用 std::wstring 和相关的 std::iostream 函数,请注意系统区域设置和 std::locale 设置。

Mac OS X 使用 UTF8 作为唯一的默认文本编码,而 Windows 使用 UTF16 LE。我认为,您还只需要一种内部文本编码,再加上几个转换功能就可以了。

【讨论】:

  • 很公平。 ICU 的问题是它很重,我试图尽可能避免使用外部库。我的目标是静态链接的可执行文件(即没有外部 dll 或 so 文件)低于 300K,所以我真的负担不起链接 ICU 的费用。除非我能找到一个小头文件的模板/函数集/macros,我可能最终会自己编写一些函数来转换字符串。
【解决方案2】:

经过一些研究、尝试和错误,我决定使用 UTF8-CPP,它是一个轻量级的、仅包含标头的函数集,用于与 utf8 相互转换。它包括从 utf-16 转换为 utf-8 的功能,据我了解,可以正确处理 BOM。

然后我将所有字符串存储为std::string,将 utf-16 字符串转换为 utf-8,如下所示(来自我上面的示例):

int 长度; 字符编码; char* 字节;

string value;
switch(encoding) {
    case 0x00:
    case 0x03:
        value = string(bytes, length);
        break;
    case 0x01:
    case 0x02:
        vector<unsigned char> utf8;
        wchar_t* input = (wchar_t*)bytes;
        utf16to8(input, input + (length >> 1), back_inserter(utf8));
        value = string(utf8.start(), utf8.end());
        break;
    default:
        throw ERROR_INVALID_STRING_ENCODING;
}

这在我的快速测试中运行良好。在最终判断之前,我需要做更多的测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 2014-02-24
    • 1970-01-01
    • 2016-09-04
    • 2012-08-24
    • 1970-01-01
    • 2021-09-15
    相关资源
    最近更新 更多