Каков оптимальный многоплатформенный способ иметь дело со строками Unicode под C++?

Я знаю, что уже существует несколько вопросов на StackOverflow о std::string по сравнению с std::wstring или подобный, но ни один из них не предложил полного решения.

Для получения хорошего ответа, я должен определить требования:

  • многоплатформенное использование, должен работать над Windows, OS X и Linux
  • минимальное усилие для преобразования в платформу определенный Unicode представляет в виде строки как CFStringRef, wchar_t *, char* как UTF-8 или другие типы, поскольку они требуются ОС API. Комментарий: Мне не нужна кодовая страница convertion поддержка, потому что я ожидаю использовать только Unicode совместимые функции во всех поддерживаемых операционных системах.
  • если требует внешней библиотеки, этот должен быть открытым исходным кодом и в соответствии с очень либеральной лицензией как BSD, но не LGPL.
  • смогите использовать printf синтаксис формата или подобный.
  • простой способ строкового выделения/освобождения
  • производительность не очень важна, потому что я предполагаю, что строки Unicode используются только для приложения UI.
  • некоторый пример мог цениться

Я был бы очень признателен за только одно предлагаемое решение на ответ путем выполнения этого, люди могут голосовать за свою предпочтенную альтернативу. Если у Вас есть больше чем одна альтернатива, просто добавляют другой ответ.

Укажите на что-то, что сделало работавший для Вас.

Связанные вопросы:

19
задан Community 23 May 2017 в 12:16
поделиться

5 ответов

Мне кажется, что у вас на самом деле есть два разных поведения в одном классе.

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

Лично я все для разоблачения обоих итераторов (т. Е. Вперед начинается, конец, rbegin и Rind ).

Вы также можете вернуть простой объект ITERATOR:

template <class T>
class Iterator
{
public:
  typedef typename T::reference_type reference_type;

  Iterator(T it, T end) : m_it(it), m_end(end) {}

  operator bool() const { return m_it != m_end; }

  reference_type operator*() const { return *m_it; }

  Iterator& operator++() { ++m_it; return *this; }

private:
  T m_it;
  T m_end;
};

template <class T>
Iterator<T> make_iterator(T it, T end) { return Iterator<T>(it,end); }

затем вы можете просто вернуть этот простой объект:

class Action
{
public:
  Action(std::deque<Move> const& d): m_deque(d) {} // const& please

  typedef Iterator< std::deque<Move>::iterator > forward_iterator_type;
  typedef Iterator< std::deque<Move>::reverse_iterator > backward_iterator_type;

  forward_iterator_type forward_iterator()
  {
    return make_iterator(m_deque.begin(), m_deque.end());
  }

  backward_iterator_type backward_iterator()
  {
    return make_iterator(m_deque.rbegin(), m_deque.rend());
  }


private:
  std::deque<Move> m_deque;
};

или если вы хотите динамически выбрать между передней и обратной траверсом, вы можете сделать итератор чистый виртуальный интерфейс и Имея вперед, так и назад.

Но на самом деле я на самом деле не вижу смысла хранить вперед и назад и обратный итератор, если окажется, что вы будете использовать только один: /

-121--1141568-

То же, что и ADAM Rosenfield Action (+1), но я использую UTFCPP вместо этого.

5
ответ дан 30 November 2019 в 05:04
поделиться

Я был недавно в проекте, который решил использовать STD :: WSTRing для кроссплатформенного проекта, потому что «Широкие строки - это Unicode, верно?» Это привело к ряду головных болей:

  • Насколько велика скалярная ценность в ветке? Ответ: Это зависит от реализации компилятора. В Visual Studio (Win) это 16 бит. Но в Xcode (Mac) это 32 бита.
  • Это привело к несчастным решениям для использования UTF-16 для связи по проводу. Но какой UTF-16? Есть два: UTF-16BE (Big-Endian) и UTF16-LE (Little-Endian). Не понятно на это привело к еще больше ошибках.

Когда вы находитесь в конкретном платформе кода, имеет смысл использовать собственное представление платформы для общения с его API. Но для любого кода, который передается через платформы, или связывается между платформами, избегайте всю двусмысленность и использовать UTF-8.

2
ответ дан 30 November 2019 в 05:04
поделиться

Я бы пошел на представление UTF16 в памяти и UTF-8 или 16 на жестком диске или проводе. Основная причина: UTF16 имеет фиксированный размер для каждой «буквы». Это упрощает много обязанностей при работе со строкой (поиск, замена деталей, ...).

Единственная причина для UTF-8 - снижение использования памяти для букв «Western / Latin». Вы можете использовать это представление для дискового хранения или транспортировки по сети. Он также имеет преимущество, которое вам не нужно беспокоиться о том, как загрузка / сохранение на диск / провод.

По мнению этих причин, я бы пошел на STD :: Wstring внутри или - если ваша библиотека GUI предлагает широкоподъемное, используйте это (например, QString от Qt). И для хранения дисков я бы написал небольшую платформу независимую обертку для платформы API. Или я бы проверил Unicode.org, если у них есть платформерный код, доступный для этого преобразования.


Для пояснения: корейские / японские буквы не являются западными / латыними. Японцы для экзамена Kanji. Вот почему я упомянул набор латинского персонажа.


Для UTF-16 не является 1 символом / 2 байта. Это предположение верно только для персонажей, находящихся на базовой многоязычной плоскости (см.: http://en.wikipedia.org/wiki/utf16 ). Еще большинство пользователей UTF-16 предполагают, что все символы на BMP. Если это не может быть гарантировано для вашего приложения, вы можете переключиться на UTF32 или переключиться на UTF8.

Все еще UTF-16 используется по причинам, упомянутым выше во многих API (например, WXW, QT, Java, .NET, WXWidgets)

1
ответ дан 30 November 2019 в 05:04
поделиться

Я настоятельно рекомендую использовать UTF-8 внутри вашего приложения, используя регулярные старые Char * или STD :: string для хранения данных. Для взаимодействия с API, которые используют другое кодирование (ASCII, UTF-16 и т. Д.), Я бы порекомендовал использовать libiconv , который лицензирован под LGPL.

Пример использования:

class TempWstring
{
public:
  TempWstring(const char *str)
  {
    assert(sUTF8toUTF16 != (iconv_t)-1);
    size_t inBytesLeft = strlen(str);
    size_t outBytesLeft = 2 * (inBytesLeft + 1);  // worst case
    mStr = new char[outBytesLeft];
    char *outBuf = mStr;
    int result = iconv(sUTF8toUTF16, &str, &inBytesLeft, &outBuf, &outBytesLeft);
    assert(result == 0 && inBytesLeft == 0);
  }

  ~TempWstring()
  {
    delete [] mStr;
  }

  const wchar_t *Str() const { return (wchar_t *)mStr; }

  static void Init()
  {
    sUTF8toUTF16 = iconv_open("UTF-16LE", "UTF-8");
    assert(sUTF8toUTF16 != (iconv_t)-1);
  }

  static void Shutdown()
  {
    int err = iconv_close(sUTF8toUTF16);
    assert(err == 0);
  }

private:
  char *mStr;

  static iconv_t sUTF8toUTF16;
};

iconv_t TempWstring::sUTF8toUTF16 = (iconv_t)-1;

// At program startup:
TempWstring::Init();

// At program termination:
TempWstring::Shutdown();

// Now, to convert a UTF-8 string to a UTF-16 string, just do this:
TempWstring x("Entr\xc3\xa9""e");  // "Entrée"
const wchar_t *ws = x.Str();  // valid until x goes out of scope

// A less contrived example:
HWND hwnd = CreateWindowW(L"class name",
                          TempWstring("UTF-8 window title").Str(),
                          dwStyle, x, y, width, height, parent, menu, hInstance, lpParam);
7
ответ дан 30 November 2019 в 05:04
поделиться

Недостатком метода является то, что потребляющий код должен знать, какие ключи использовать для места хранения и извлечения. Это может быть причиной ошибки, так как ключ должен быть точно правильным, или вы рискуете сохранить в неправильном месте, или получить нулевое значение назад.

Я на самом деле использую сильно типизированный вариант, так как я знаю, что мне нужно иметь в сеансе, и, таким образом, могу настроить класс обертывания на соответствие. У меня скорее есть дополнительный код в сеансовом классе, и не нужно беспокоиться о ключевых последовательностях где-либо еще.

-121--4321269-

Рекомендуется инициализировать память/переменные до их использования - неинициализированные переменные являются большим источником ошибок, которые часто очень трудно отследить.

Инициализация всех данных является очень хорошей идеей при записи их в формат файла: Он обеспечивает чистоту содержимого файла, так что они легче работать с, менее склонны к проблемам, если кто-то неправильно пытается «использовать» неинициализированные данные (помните, что это может быть не только ваш собственный код, который считывает данные в будущем), и делает файлы гораздо более сжимаемыми.

Единственная веская причина не инициализировать переменные до их использования - это критические для производительности ситуации, когда инициализация технически «не нужна» и влечет за собой значительные издержки. Но в большинстве случаев инициализация переменных не причинит существенного вреда (особенно если они объявлены непосредственно перед их использованием), но сэкономит вам много времени на разработку, устранив общий источник ошибок.

-121--3223106-

Правило: используйте для обработки форму Юникод собственной платформы (UTF-16 или UTF-32), а для обмена данными (связь, место хранения) - UTF-8.

Если все собственные API используют UTF-16 (например, в Windows), имея свои последовательности в качестве UTF-8 означает, что вам придется конвертировать все входные данные в UTF-16, вызвать Win API, а затем преобразовать ответ в UTF-8. Довольно боль.

Но если основной проблемой является пользовательский интерфейс, то последовательности являются простой проблемой. Более сложной является структура пользовательского интерфейса. И для этого я бы рекомендовал wxWidgets ( http://www.wxWidgets.org ). Поддерживает множество платформ, зрелые (17 лет и все еще очень активные), родные виджеты, Unicode, либеральную лицензию.

1
ответ дан 30 November 2019 в 05:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: