#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;
}