#windows #unicode #console #visual_studio #cpp
Мне нужно создать консольное приложение, которое Считывало бы введённые пользователем символы: как англоязычные, так и русскоязычные. Выводила бы эти символы на экран в консоль. Записывала бы эти символы в файл. С первым и вторым пунктом, как убрать кракозябры, я разобрался следующим образом: Подключил #includeВесь вывод стал пропускать через функцию ToRus т.е. cout<< ToRus(L"Почему всё так сложно!") Вот сама функция: char *ToRus(wchar_t *str) { static char s[1024]; CharToOem(LPCWSTR(str), s); return s; } Но осталась проблема 3-м пунктом - с выводом в файл. В файл по-прежнему выводятся кракозябры. Подскажите, пожалуйста, как решить проблему, как сделать так, чтобы в файл нормально выводилась? Может вообще, как-то по-другому нужно было делать?
Ответы
Ответ 1
Нашёл! Вот вам статья: http://www.codeproject.com/Articles/4563/Upgrading-an-STL-based-application-to-use-Unicode. (Не смотрите часть с tstream/TCHAR, вам нужно просто wstream.) Или то же здесь: http://forums.codeguru.com/showthread.php?457106-Unicode-text-file&p=1741409#post1741409. Проблема в том, что обыкновенный wfstream принимает Unicode-строки, а вот пишет их как не-Unicode. Но его можно заставить. Вот вам вкратце описание, как. Первый шаг: создаёте «пустой» конвертер. В обычном случае wchar_t* конвертируется с потерями в обычные символы, но нам надо эту конвертацию отключить. Код конвертера: #include#include #include #include #include typedef std::codecvt null_wcodecvt_base; class null_wcodecvt : public null_wcodecvt_base { public: explicit null_wcodecvt(size_t refs = 0) : null_wcodecvt_base(refs) {} protected: virtual result do_out(mbstate_t&, const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, char* to, char* to_end, char*& to_next) const { size_t len = (from_end - from) * sizeof(wchar_t); memcpy(to, from, len); from_next = from_end; to_next = to + len; return ok; } virtual result do_in(mbstate_t&, const char* from, const char* from_end, const char*& from_next, wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const { size_t len = (from_end - from); memcpy(to, from, len); from_next = from_end; to_next = to + (len / sizeof(wchar_t)); return ok; } virtual result do_unshift(mbstate_t&, char* to, char*, char*& to_next) const { to_next = to; return noconv; } virtual int do_length(mbstate_t&, const char* from, const char* end, size_t max) const { return (int)((max < (size_t)(end - from)) ? max : (end - from)); } virtual bool do_always_noconv() const throw() { return true; } virtual int do_encoding() const throw() { return sizeof(wchar_t); } virtual int do_max_length() const throw() { return sizeof(wchar_t); } }; Теперь, надо ещё переопределить endl. std::wostream& wendl(std::wostream& out) { out.put(L'\r'); out.put(L'\n'); out.flush(); return out; } А вот и пример использования. Не забудьте про BOM (такая штука в начале файла, может и не понадобиться, если не под Windows). const wchar_t UTF_BOM = 0xfeff; int main() { // заводим файл std::wfstream file; // инстанциируем пустой конвертер null_wcodecvt wcodec(1); std::locale wloc(std::locale::classic(), &wcodec); // сообщаем файлу, что он должен использовать этот конвертер, // а не стандартный. обязательно делаем это _до_ открытия файла file.imbue(wloc); // открыли файл, обязательно как binary, чтобы никакой самодеятельности // от стандартной библиотеки file.open("data.txt", std::ios::out | std::ios::binary); if (!file) { // если текст русский, надо прогнать через `ToRus` std::cerr << "Failed to open data.txt for writing" << std::endl; return 1; } // записали в начало файла BOM file << UTF_BOM; // записали текст file << L"Я тоже не знаю, почему в C/C++ всё так сложно." << wendl; // закрыли файл file.close(); // фсё! return 0; }
Комментариев нет:
Отправить комментарий