Читать / хранить различные типы строк (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;
}

Поскольку я чуть больше распечатаю эти строки позже, я могу хранить UTF8 в простом string без особых проблем.

У меня есть два вопроса:

  1. Является ли такой подход разумным (то есть с использованием boost :: Вариант)?

  2. Как мне создать wstring с конкретной спецификацией?

1

Решение

UTF16 нужно различать между LE и BE.

Я подозреваю 0x02 - utf-16 without BOM (wchar_t* directly) это на самом деле UTF16 BE. With BOM означает, что кодировка LE / BE указана в спецификации.

Поддержка Unicode стандартной библиотеки C ++ очень ограничена, и я не думаю, что 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. Вам также понадобится только одна внутренняя кодировка текста, плюс, я думаю, вам пригодятся несколько функций преобразования.

0

Другие решения

После некоторых исследований, попыток и ошибок я решил использовать UTF8-CPP, который представляет собой легкий набор функций только для заголовков для преобразования в / из utf8. Он включает в себя функции для преобразования из utf-16 в utf-8 и, насколько я понимаю, может корректно работать с спецификацией.

Затем я храню все строки как 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;
}

Это хорошо работает в моем быстром тесте. Мне нужно сделать больше испытаний перед окончательным решением.

0

По вопросам рекламы [email protected]