Почему необходимо наполнять поток кодированием фиксированной длины? Кроме того, как я могу предотвратить утечку памяти при наполнении?

На днях я писал код, похожий на:

wchar_t buffer[1024];
std::wifstream input(L"input.txt");

while (input.good())
{
    input::getline(buffer, 1024);
    // ... do stuff...
}

input.close();

Я обнаружил, что после первого вызова getline, buffer содержал правильные байты данных (UTF-16 LE), но вместо того, чтобы buffer воспринимался как массив wchar_t, он волшебным образом преобразовывался в массив байтов. Я reinterpret_cast<wchar_t *>(buffer) и получил желаемый результат.

Затем следующий вызов getline... на этот раз буфер снова воспринимался как массив байтов, но байты были искажены. Я ожидал увидеть 0x31 0x00 0x32 0x00 0x33 0x00, но вместо этого увидел 0x00 0x31 0x00 0x32 0x00 0x33

Теперь я могу понять, как все может быть искажено, если символы имеют кодировку переменной длины... но ВСЕ символы в моем файле input.txt являются ASCII и, следовательно, могут быть закодированы с помощью 2 байтов каждый (с использованием UTF16-LE). Почему перекос?

Ответчик на SO сообщил мне, что я должен наполнить поток так:

std::wifstream fin("text.txt", std::ios::binary);
// apply facet
fin.imbue(std::locale(fin.getloc(),
          new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>));

Действительно, это полностью решило мою проблему. Я не понимаю, зачем нужно наполнение, если все символы, с которыми вы имеете дело, имеют кодировку фиксированной длины?

Во-вторых, второй параметр для наполнения похоже на утечку памяти?! Если я выделяю объект std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian> в стеке и передаю его адрес в imbue, кажется, что все работает до тех пор, пока моя переменная стека не выйдет за пределы области видимости (прямо перед основной закрывающей скобкой). Приложение вылетает, жалуясь, что какая-то функция вызывает чисто виртуальную функцию. Я вижу такое же поведение, если я использую предоставленный код и вместо этого вызываю удаление в памяти до того, как main вернется.

Заранее спасибо за ваши комментарии и ответы.


person charunnera    schedule 01.11.2013    source источник


Ответы (1)


Ваш файл содержит что-то вроде 31 00 32 00 33 00 0A 00 34 00 .... 0A — символ перевода строки.

С фасетом по умолчанию codecvt каждый байт отдельно преобразуется в Unicode. Итак, 31 становится U+0031, 00 становится U+0000 и так далее. getline останавливается на 0A байте.

Следующий вызов getline продолжается с того места, где остановился предыдущий: 00 становится U+0000 и так далее.

person Igor Tandetnik    schedule 01.11.2013
comment
Теперь я понимаю. (Извините, я не установил соединение во время вашего первоначального ответа). Что касается проблемы с утечкой памяти, похоже, это проблема, которая была решена Microsoft в VS 2012 Update 3 [ссылка] connect.microsoft.com/VisualStudio/feedback/details/650567 Еще раз спасибо. - person charunnera; 01.11.2013