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

问题描述:

我正在解析一个文件,其中包含不同编码中的各种字符串。这些字符串的存储方式是这样的:读取/存储不同类型的字符串(utf8/utf16/ansi)

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

当我做多一点以后再打印这些字符串,我可以在一个简单的string存储UTF8没有太多的麻烦。

这两个问题我有是:

  1. 就是这样的方法(即使用boost ::变体)是合理的?

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

+0

看看这里:http://*.com/questions/402283/stdwstring-vs-stdstring(顶部的答案),如果你在Windows上只有wstring是一个可靠的选择,我的意思是整个软件不是变种方法,如果你计划跨平台,我建议使用QT的文本转换功能(处理所有在QString中) – Najzero

+0

@Najzero我在linux上开发,但结果必须能够在windows,linux和mac下编译os x。此外,请注意,我的目标是在任何平台(不包括外部要求,我不控制这些)的300K静态编译可执行文件,因此链接ICU或QT很可能不是一种选择。 –

经过一番研究,尝试和错误之后,我决定使用UTF8-CPP,这是一个轻量级的,仅用于标题的函数集,用于转换为utf8。它包括从utf-16转换为utf-8的功能,并且据我所知,可以正确处理BOM。

然后我存储所有字符串作为std::string,转换UTF-16字符串为UTF-8,是这样的(从上面的示例所示):

INT长度; char编码; 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; 
} 

这在我的快速测试中正常工作。在做出最终判断之前,我需要做更多的测试。

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

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

对C++标准库的Unicode支持非常有限,我不认为香草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。你也只需要一个内部的文本编码,再加上几个转换函数就可以达到目的,我想。

+0

够公平的。 ICU的问题在于它很重,我试图尽可能避免外部库。我瞄准一个静态链接的可执行文件(即没有外部DLL或如此的文件)在300K以下,所以我不能真正负担链接ICU。除非我能找到一个小的头文件 - 只有一套模板/函数/宏,我可能最终会自己写几个函数来转换字符串。 –