С++ вопросы по юникоду

Я знаю о ICU и небольших библиотеках, таких как utf8, в проекте кода (забудьте точное название), однако ни одна из них не совсем то, что я хочу.

Чего я действительно хочу, так это что-то вроде ICU, но в более дружелюбной манере.

Конкретно:

  • Полностью объектно-ориентированный
  • Реализации стандартных потоков С++ или, по крайней мере, что-то, что выполняет ту же роль.
  • Может форматировать время, даты и т. д. в зависимости от региона (например, дд/мм/гг в Великобритании и мм/дд/гг в США).
  • Позволяет мне выбрать «внутреннюю» кодировку строк, поэтому я могу, например, использовать UTF-16 в Windows, чтобы избежать большого количества преобразований при передаче строк в Windows API и DirectX и обратно.
  • Простое преобразование строк между кодировками

Если такой библиотеки не существует, можно ли обернуть ICU, используя стандартные классы С++, поэтому я могу, например, создать ustring, который имеет такое же использование, как std::string и std::wstring, а также реализовать версии потоков (оптимально, чтобы они были полностью совместимы с существующими, т.е. я мог бы передать его функции, ожидающей std::ostream, и она будет выполнять преобразование между своим внутренним форматом и ascii (или utf-8) на лету)? Если предположить, что это возможно, сколько работы это будет?

РЕДАКТИРОВАТЬ: Кроме того, просмотрев стандарт С++ 0x и заметив литералы для utf8, utf16 и utf32, означает ли это, что стандартная библиотека (например, строки, потоки и т. д.) будет полностью поддерживать эти кодировки и преобразование между ними? Если да, то кто-нибудь знает, сколько времени пройдет, пока Visual Studio не будет поддерживать эти функции?

EDIT2: Что касается использования существующей поддержки С++, я посмотрю локаль и фасет.

Одна из проблем, с которой я столкнулся, заключается в том, что при использовании потоков, определенных вокруг wchar_t, который составляет 2 байта под окнами для файлового ввода-вывода, однако, похоже, он по-прежнему использует ascii для самих файлов.

std::wofstream file(L"myfile.txt", std::ios::out);
file << L"Hello World!" << std::endl;

привел к следующему шестнадцатеричному коду в файле
48 65 6C 6C 6F 20 57 6F 72 6C 64 0D 0A
который явно является ascii, а не ожидаемым выводом utf-16:
FF FE 48 00 65 00 6C 00 6C 00 6F 00 20 00 57 00 6F 00 72 00 6C 00 64 00 0D 00 0A 00


person Fire Lancer    schedule 07.05.2009    source источник
comment
Текст UTF-16 на самом деле конвертируется в локальную 8-битную кодировку! Таким образом, вы не пишете utf-16 в файл. Не забудьте вызвать std::locale::global(std::locale());   -  person Artyom    schedule 07.05.2009
comment
Итак, как мне сказать, в какой кодировке я хочу файл? Я попробовал std::local... вы упомянули выше, но, похоже, это не имеет никакого эффекта :(   -  person Fire Lancer    schedule 07.05.2009
comment
Ок, например если локаль системы ru_RU.UTF-8 то кодировка utf-8 если ru_RU.KOI-8 то KOI. Вы также можете указать другую локаль: locale::globale(locale("de_DE.ISO-8859-1")); (Обратите внимание, я использую имена локалей POSIX, для Windows вы должны проверить, какие имена локалей   -  person Artyom    schedule 07.05.2009
comment
Итак, как мне получить текущую локаль (en_Us, en_Uk, что угодно) и сделать ее utf-16 для широкого файлового ввода-вывода (и assci/utf-8 для узких потоков)   -  person Fire Lancer    schedule 08.05.2009


Ответы (6)


То, что я действительно хочу, это что-то вроде ICU, но в более дружелюбной манере.

К сожалению, такого нет. Их API не НАСТОЛЬКО ужасен, так что к нему можно привыкнуть, приложив некоторые усилия.

Может форматировать время, даты и т. д. в зависимости от региона (например, дд/мм/гг в Великобритании и мм/дд/гг в США).

Его полная поддержка есть в классе std::locale, читайте о том, как его использовать. Вы также можете указать локаль для std::iostream, чтобы он правильно форматировал числа и даты.

Простое преобразование строк между кодировками

std::locale предоставляет фасеты для преобразования 8-битной локальной кодировки в широкую и обратно.

поэтому я могу, например, использовать UTF-16

ICU использует utf-16 внутри, win32 wchar_t и wstring также используют utf-16, в других ОС большинство реализаций дают wchar_t как utf-32, а wstring использует utf-32.

Примечания: Поддержка std::locale не идеальна, но она уже предоставляет множество инструментов, полезных для манипуляций с символами.

См.: http://www.cplusplus.com/reference/std/locale/

person Artyom    schedule 07.05.2009

Вот как я использую ICU для преобразования между std::string (в UTF-8) и std::wstring

/** Converts a std::wstring into a std::string with UTF-8 encoding.
 */
template < typename StringT >
StringT utf8 ( std::wstring const & rc_string );

/** Converts a std::String with UTF-8 encoding into a std::wstring.
 */
template < typename StringT >
StringT utf8 ( std::string const & rc_string );

/** Nop specialization for std::string.
 */
template < >
inline std::string utf8 ( std::string const & rc_string )
{
  return rc_string;
}

/** Nop specialization for std::wstring.
 */
template < >
inline std::wstring utf8 ( std::wstring const & rc_string )
{
  return rc_string;
}

template < >
std::string utf8 ( std::wstring const & rc_string )
{
  std::string result;
  if(rc_string.empty())
    return result;

  std::vector<UChar> buffer;

  result.resize(rc_string.size() * 3); // UTF-8 uses max 3 bytes per char
  buffer.resize(rc_string.size() * 2); // UTF-16 uses max 2 bytes per char

  UErrorCode status = U_ZERO_ERROR;
  int32_t len = 0;

  u_strFromWCS(
    &buffer[0],
    buffer.size(),
    &len,
    &rc_string[0],
    rc_string.size(),
    &status
  );
  if(!U_SUCCESS(status))
  {
    throw XXXException("utf8: u_strFromWCS failed");
  }
  buffer.resize(len);

  u_strToUTF8(
    &result[0],
    result.size(),
    &len,
    &buffer[0],
    buffer.size(),
    &status
  );
  if(!U_SUCCESS(status))
  {
    throw XXXException("utf8: u_strToUTF8 failed");
  }
  result.resize(len);

  return result;
}/* end of utf8 ( ) */


template < >
std::wstring utf8 ( std::string const & rc_string )
{
  std::wstring result;
  if(rc_string.empty())
    return result;

  std::vector<UChar> buffer;

  result.resize(rc_string.size());
  buffer.resize(rc_string.size());

  UErrorCode status = U_ZERO_ERROR;
  int32_t len = 0;

  u_strFromUTF8(
    &buffer[0],
    buffer.size(),
    &len,
    &rc_string[0],
    rc_string.size(),
    &status
  );
  if(!U_SUCCESS(status))
  {
    throw XXXException("utf8: u_strFromUTF8 failed");
  }
  buffer.resize(len);

  u_strToWCS(
    &result[0],
    result.size(),
    &len,
    &buffer[0],
    buffer.size(),
    &status
  );
  if(!U_SUCCESS(status))
  {
    throw XXXException("utf8: u_strToWCS failed");
  }
  result.resize(len);

  return result;
}/* end of utf8 ( ) */

Использовать его так же просто:

std::string s = utf8<std::string>(std::wstring(L"some string"));
std::wstring s = utf8<std::wstring>(std::string("some string"));
person lothar    schedule 07.05.2009
comment
Одна ошибка: UTF-8 использует максимум 4 байта на символ. Одно неправильное использование термина: UTF-16 использует максимум 2 кодовых единицы на символ. - person dalle; 08.10.2010

Я всегда работаю так:

поток байтов в какой-то кодировке -> ICU -> wistream -> stl & boost -> wostream -> ICU -> поток байтов в какой-то кодировке

person puchu    schedule 09.10.2011

Форматирование даты, времени и т. д. можно выполнить, указав конкретную локаль. Что касается создания собственного — это всегда возможно, взяв столько или меньше из базовой библиотеки, сколько вам нужно.

Кроме того, просмотрев стандарт С++ 0x и заметив литералы для utf8, utf16 и utf32, означает ли это, что стандартная библиотека (например, строки, потоки и т. д.) будет полностью поддерживать эти кодировки и преобразование между ними?

да. Но обратите внимание, что это разные типы данных, а не ваша обычная последовательность wchar или wstring.

Если да, то кто-нибудь знает, сколько времени пройдет, пока Visual Studio не будет поддерживать эти функции?

Насколько мне известно: vc9 (VS2008) имеет лишь частичную поддержку некоторых функций TR1. Ожидается, что vc10 (VS2010) будет иметь лучшую поддержку.

person dirkgently    schedule 07.05.2009
comment
Да, но он не форматирует его в определенную кодировку, конечно, я мог бы отформатировать его в строку ascii, а затем закодировать, но что, если бы я хотел использовать длинные названия месяцев в китайском языке, что невозможно в ascii? - person Fire Lancer; 07.05.2009
comment
Вот тут-то и вступает в игру кодирующая часть локали. Кроме того, посмотрите на грани. - person dirkgently; 07.05.2009
comment
да. Значительно недоиспользуется локальная функциональность. Не применяйте формат к своему пользователю. Позвольте системе определить формат, все, что вам нужно сделать, это убедиться, что локаль установлена ​​​​правильно, тогда поток будет работать правильно. (+1) - person Martin York; 07.05.2009
comment
Но обратите внимание, что это разные типы данных, а не обычная последовательность wchar или wstring. поэтому, когда я создаю класс с перегруженными операторами ›› и ‹‹, мне теперь придется написать реализацию для char, wchar_t и каждого из типов данных Unicode (при условии, что я не использую шаблоны, так как они могут не хотеться в заголовке, но вместо этого, скажем, dll)? Или будет какой-то общий тип потока? - person Fire Lancer; 07.05.2009
comment
Нет, с C++0x вы должны использовать эти новые типы, а не wchar_t или wstring. - person dirkgently; 07.05.2009

Я сделал свою маленькую обертку. Могу поделиться, если хотите.

person piotr    schedule 07.05.2009
comment
Поддерживает ли он потоки С++, потому что моя основная проблема с ICU и тот факт, что у меня есть очень большое приложение, которое я хочу заставить работать с юникодом. - person Fire Lancer; 07.05.2009

Вот незадача. Я знаю, что библиотеки Dinkumware предлагают некоторую поддержку Unicode — вы можете посмотреть документацию на их веб-сайте. AFAIK, это не бесплатно.

person Nemanja Trifunovic    schedule 07.05.2009