Не получается преобразование нуль-терминальной UTF8 в std::wstring

Общие вопросы, не зависящие от языка реализации.

Модераторы: Duncon, Hawk, Romeo, Eugie

Аватара пользователя
Сионист
Сообщения: 1077
Зарегистрирован: 31 мар 2014, 06:18

12 дек 2015, 19:47

Есть указатель на char, в котором лежит нуль-терминальная строка в UTF8. Пока в строке только латиница, или только кириллица, преобразование происходит правильно. Но как только смешаны двубайтные символы с однобайтными, начинаются странности. Например, строка равна "Система>Как Вас зовут?" (префикс опущен, но это UTF-8),

Код: Выделить всё

L16=MultiByteToWideChar(CP_UTF8, 0, Utf8, L8, nullptr, 0);
Пока в строке только латиница, или только кириллица, L8 равна 41 (правильно), но L16 вдруг 27, дальше

Код: Выделить всё

MultiByteToWideChar(CP_UTF8, 0, Utf8, L8, Utf16, L16);
return std::wstring(Utf16);
, 26 символов, но TextOut пишет слово L"Система" и потом квадратик вместо символа L'>' и дальше символов нет.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Сионист
Сообщения: 1077
Зарегистрирован: 31 мар 2014, 06:18

12 дек 2015, 20:42

Проблема решена посимвольным преобразованием. Конкатенация уже преобразованных строк работала правильно с самого начала, но только если сами строки преобразованы правильно. Смешение двубайтных и однобайтных символов при конкатенации значения не имело.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1213
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

13 дек 2015, 01:25

А немного подумать нельзя было?

Код: Выделить всё

template<std::size_t BUFF_SIZE = 0x100>
  std::wstring toWideChar(const char* src, UINT codepage = CP_UTF8)
{
  enum { MaxStackBuffSizeInWchars = BUFF_SIZE / sizeof(wchar_t) };
  int srcLengthInBytes = static_cast<int>(std::strlen(src));
  int lengthInWideChars = MultiByteToWideChar(codepage, 0, src, srcLengthInBytes, NULL, 0);
  if (lengthInWideChars <= MaxStackBuffSizeInWchars) {
    wchar_t buff[MaxStackBuffSizeInWchars];
    MultiByteToWideChar(codepage, 0, src, srcLengthInBytes, buff, lengthInWideChars);
    return std::wstring(buff, lengthInWideChars);
  } else {
    auto buff = std::make_unique<wchar_t[]>(lengthInWideChars);
    MultiByteToWideChar(codepage, 0, src, srcLengthInBytes, buff.get(), lengthInWideChars);
    return std::wstring(buff.get(), lengthInWideChars);
  }
}
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1077
Зарегистрирован: 31 мар 2014, 06:18

13 дек 2015, 08:03

И что это?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1213
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

13 дек 2015, 13:34

Преобразование нуль-терминальной UTF8 в std::wstring.

Код: Выделить всё

std::wstring ws = toWideChar("Шалом Израиль"); //Подразумевается что исходный код сохранен в UTF8
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1077
Зарегистрирован: 31 мар 2014, 06:18

13 дек 2015, 13:40

1. Начнём с не понятного буферсайза. С какого потолка он вообще берётся?
2. Каков смысл этого енама?
3. Зачем здесь уникуй?
4. Где и как в ветви else выделяется память для результата преобразования?
5. А освобождается она где и как?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1213
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

13 дек 2015, 14:31

Начнём с не понятного буферсайза. С какого потолка он вообще берётся
А его можно не трогать. Там прописан дефолт - 256 байт, то есть для строк чья длина меньше 256 байт динамическая память вообще не выделяется. Преобразование происходит в стеке. Если больше 256 байт, он создает временный массив в хипе.
Каков смысл этого енама
Это внутренняя константа внутри функции. Ее область видимости ограничена функцией. Вероятно можно использовать constexpr или что-то в этом роде.
Зачем здесь уникуй
Чтобы удалить временный массив после выхода из функции, очевидно.
Где и как в ветви else выделяется память для результата преобразования
Это делает make_unique.
А освобождается она где и как
Деструктор unique_ptr

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

Код: Выделить всё

std::string toMultibyte(const wchar_t* src, UINT codepage = CP_UTF8)
{
  int wcharCount = static_cast<int>(std::wcslen(src));
  int buffSize = WideCharToMultiByte(codepage, 0, src, wcharCount, NULL, 0, NULL, NULL);
  char* buff = static_cast<char*>(_alloca(buffSize));
  WideCharToMultiByte(codepage, 0, src, wcharCount, buff, buffSize, NULL, NULL);
  return std::string(buff, buffSize);
}
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1077
Зарегистрирован: 31 мар 2014, 06:18

13 дек 2015, 15:51

Это внутренняя константа внутри функции.
Ну локальные типы от глобальных я отличаю без посторонней помощи. Вопрос не в этом, а в назначении.
Absurd писал(а):А его можно не трогать. Там прописан дефолт - 256 байт, то есть для строк чья длина меньше 256 байт динамическая память вообще не выделяется.
С какого потолка этот дефолт списан и зачем?
Absurd писал(а):Чтобы удалить временный массив после выхода из функции, очевидно.
И как это удаление соотносится с уникальностью значений?
Это делает make_unique.

А освобождается она где и как
Деструктор unique_ptr
Не понял. Нельзя ли по подробней?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1213
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

13 дек 2015, 16:31

Сионист писал(а):Ну локальные типы от глобальных я отличаю без посторонней помощи. Вопрос не в этом, а в назначении.
Назначение описано в названии константы.
Если интересует зачем я вычисляю размер буфера в широких чарах из параметра шаблона,
то причина в том что у этой функции есть обратная и у нее промежуточный буфер не в чарах а байтах.
Чтобы иметь общий вид у обоих функций размер буфера задается в байтах.

Код: Выделить всё

template<std::size_t BUFF_SIZE = 0x100>
  std::string toMultibyte(const wchar_t* src, UINT codepage = CP_UTF8)
{
  int wcharCount = static_cast<int>(std::wcslen(src));
  int buffSize = WideCharToMultiByte(codepage, 0, src, wcharCount, NULL, 0, NULL, NULL);
  if (buffSize <= BUFF_SIZE) {
    char buff[BUFF_SIZE];
    WideCharToMultiByte(codepage, 0, src, wcharCount, buff, buffSize, NULL, NULL);
    return std::string(buff, buffSize);
  } else {
    auto buff = std::make_unique<char[]>(buffSize);
    WideCharToMultiByte(codepage, 0, src, wcharCount, buff.get(), buffSize, NULL, NULL);
    return std::string(buff.get(), buffSize);
  }
}
Сионист писал(а):С какого потолка этот дефолт списан и зачем?
Если не устраивает дефолт размер можно передать явно через параметр шаблона.
Но это оптимизация. Функция-то совершенно корректно обрабатывает строки любой длины.
Только длинные строки - медленнее и с привлечением дополнительной памяти.
Что считать длинной строкой - конфигурируется.
Сионист писал(а):И как это удаление соотносится с уникальностью значений?
unique_ptr - уникальный указатель, а не значение.
Сионист писал(а):
Деструктор unique_ptr
Не понял. Нельзя ли по подробней?
Деструктор unique_ptr делает delete[] для массивов, ничего непонятного тут нет
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1077
Зарегистрирован: 31 мар 2014, 06:18

13 дек 2015, 18:13

Absurd писал(а):Назначение описано в названии константы.
Какой константы? У Вас их вообще нет.
Absurd писал(а):Если не устраивает дефолт размер можно передать явно через параметр шаблона.
А где его вообще брать?
Absurd писал(а):unique_ptr - уникальный указатель, а не значение.
А где там вообще ptr то?
Absurd писал(а):Деструктор unique_ptr делает delete[] для массивов, ничего непонятного тут нет
А где массив?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Ответить