Ошибка вывода потока Windows Unicode C ++ - PullRequest
11 голосов
/ 25 марта 2012

В настоящее время я пишу приложение, которое требует от меня вызова GetWindowText в произвольных окнах и сохранения этих данных в файле для последующей обработки.Короче говоря, я заметил, что мой инструмент не работал в Battlefield 3, и я сузил проблему до следующего символа в заголовке окна: http://www.fileformat.info/info/unicode/char/2122/index.htm

Итак, я создал небольшое тестовое приложение, которое просто выполняетследующее:

std::wcout << L"\u2122";

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

Почему MSVC STL задыхается от этого символа (и я полагаю, другие), когдаТакие API, как MessageBoxW и т. Д., Отображают его просто отлично?

Как можно распечатать эти символы в моем файле?

Протестировано на VC10 и VC11 под Windows 7 x64.

Извинитеиз-за плохо сконструированного поста я тут рву свои волосы.

Спасибо.

РЕДАКТИРОВАТЬ:

Минимальный тестовый пример

#include <fstream>
#include <iostream>

int main()
{
  {
    std::wofstream test_file("test.txt");
    test_file << L"\u2122";
  }

  std::wcout << L"\u2122";
}

Ожидаетсярезультат: символ '™' напечатан на консоли и в файле.Наблюдаемый результат: файл создан, но пуст.Нет вывода на консоль.

Я подтвердил, что шрифт, который я использую для своей консоли, способен отображать соответствующий символ, и файл определенно пуст (размером 0 байт).

РЕДАКТИРОВАТЬ:

Дальнейшая отладка показывает, что 'failbit' и 'badbit' установлены в потоке (ах).

EDIT:

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

Ответы [ 4 ]

14 голосов
/ 26 марта 2012

Чтобы записать в файл, вы должны правильно задать локаль, например, если вы хотите записать их как символы UTF-8, вы должны добавить

const std::locale utf8_locale
            = std::locale(std::locale(), new std::codecvt_utf8<wchar_t>());
test_file.imbue(utf8_locale);

Вы должны добавить эти 2включаемые файлы

#include <codecvt>
#include <locale>

Для записи в консоль необходимо установить консоль в правильном режиме (это зависит от Windows), добавив

_setmode(_fileno(stdout), _O_U8TEXT);

(если вы хотите использоватьUTF-8).

Для этого вам нужно добавить эти 2 включаемых файла:

#include <fcntl.h>
#include <io.h>

Кроме того, вы должны убедиться, что вы используете шрифт, поддерживающий Unicode (например, дляпример Lucida Console).Вы можете изменить шрифт в свойствах своего окна консоли.

Полная программа теперь выглядит следующим образом:

#include <fstream>
#include <iostream>
#include <codecvt>
#include <locale>
#include <fcntl.h>
#include <io.h>

int main()
{

  const std::locale utf8_locale = std::locale(std::locale(),
                                    new std::codecvt_utf8<wchar_t>());
  {
    std::wofstream test_file("c:\\temp\\test.txt");
    test_file.imbue(utf8_locale);
    test_file << L"\u2122";
  }

  _setmode(_fileno(stdout), _O_U8TEXT);
  std::wcout << L"\u2122";
}
2 голосов
/ 25 марта 2012

Я только что протестировал GCC (версии 4.4 - 4.7) и MSVC 10, которые все демонстрируют эту проблему.

В равной степени нарушен wprintf, что так же мало, как API C ++ stream.

Я также протестировал сырой Win32 API, чтобы увидеть, не вызвало ли что-либо еще ошибку, и это работает:

#include <windows.h>
int main()
{ 
    HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD n;
    WriteConsoleW( stdout, L"\u03B2", 1, &n, NULL );
}

Который пишет β в консоль (если вы установили шрифт cmd на что-то вроде Lucida Console).

Вывод: wchar_t вывод ужасно нарушен в обеих больших реализациях библиотеки C ++ Standard.

2 голосов
/ 25 марта 2012

Вы всегда используете std::wcout или иногда std::cout?Смешивать их не получится.Конечно, в описании ошибки «удушье» вообще не сказано, какую проблему вы наблюдаете.Я подозреваю, что это проблема, отличная от той, в которой используются файлы.

Поскольку нет реального описания проблемы, требуется несколько хрустальных шаров, а затем выстрел в темноте, чтобы попастьпроблема ... Так как вы хотите получить символы Unicode из вашего файла, убедитесь, что используемый вами файловый поток использует std::locale, чей аспект std::codecvt<...> фактически преобразуется в подходящую кодировку Unicode.

1 голос
/ 25 марта 2012

Хотя широкие символьные потоки принимают Unicode в качестве входных данных, это не то, что они создают в качестве выходных данных - символы проходят преобразование. Если символ не может быть представлен в кодировке, в которую он конвертируется, вывод завершается ошибкой.

...