读/存不同类型的字符串(utf8 / utf16 / ansi)

时间:2013-01-07 10:14:48

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

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

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

2 个答案:

答案 0 :(得分:0)

需要在LE与BE之间区分UTF16。

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

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

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

Mac OS X使用UTF8作为唯一的默认文本编码,而Windows使用UTF16 LE。你还需要内部只有一个文本编码,我想你会想到几个转换函数。

答案 1 :(得分:0)

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

然后我将所有字符串存储为std::string,将utf-16字符串转换为utf-8,就像这样(从上面的示例中):

int length; 字符编码; char * bytes;

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;
}

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