Хочу поднять старую и давно избитую тему об использовании русского языка в консольных приложениях. Многим кажеться, что раз на эту тему многие пишут на форумах то ответ на неё давно найден. Я тоже так думал пока пара часов гугления не убедили меня в обратном. Итак, варианты решения задачи: 1) Ручной конверт ANSI -> OEM. Выглядит это самописное счастье примерно так: Код (Text): //из ДОС в Windows char* Decode_DOS_to_Win(char * str) { unsigned char *cstr=str;//"unsigned" - чтоб избежать предупреждений комп-ра for(;*cstr;cstr++) { if(*cstr>=128 && *cstr<=175) *cstr+=64; else if(*cstr>=224 && *cstr<=239) *cstr+=16; else if(*cstr==252) *cstr=185; } return str; } //---------------------------------------------------- //из Windows в ДОС char* Decode_Win_to_DOS(char * str) { unsigned char *cstr=str; for(;*cstr;cstr++) { if(*cstr>=240) *cstr-=16; else if(*cstr>=192) *cstr-=64; else if(*cstr==185) *cstr=252; } return str; } Вывод: аналог решению 2 (см. выводы решения 2) 2) Использование CharToOEM, OEMToChar, CharToOEMBuff, OEMToCharBuff из user32.dll. Вывод: переводить каждую строчку этими функциями глупо, некрасиво, громоздко. 3)Использование Unicode. Вывод: Собственно единственное действенное решение этой задачи (работает на всей линейке Win95/WinNT). Но если в С++ вы не хотите использовать Unicode по каким-либо причинам (а таких может быть много) он вам не подойдет. 4)Использование SetConsoleCP и SetConsoleOutputCP (только для линейки NT, начиная с XP проф., но учитвая что 99% используемых "окон" и есть НТ, то фактически для всех) Вывод: На первый взгляд красивое и удобное решение проблемы, достаточно только написать где-то в начале своего консольного приложения: SetConsoleCP(1251); SetConsoleOutputCP(1251); и все по идее должно работать, однако...написанные мною на Си строчки: Код (Text): DWORD len = 5, write; printf("Set Input: %d\n",SetConsoleCP (1251)); printf("Set Output: %d\n",SetConsoleOutputCP (1251)); WriteConsole (GetStdHandle (0xfffffff5), (void *) "Вася\n", len, &write, NULL); printf("Get Input: %d\n",GetConsoleCP ()); printf("Get Output: %d\n",GetConsoleOutputCP ()); Вывело вместо ожидаемого "Васи" набор кракозябр, несмотря на то, что установление новых кодовых страниц консоли прошло успешно, и судя по значениям, возвращенным GetConsoleCP и GetConsoleOutputCP текущей кодовой страницей консоли является именно страница ANSI (1251). В MSDN в описании функции WriteConsole прямым текстом написано следующее: Что в переводе на неизвестный язык означает примерно следующее: Функция использует для вывода на консоль текущую кодовую страницу консоли. По умолчанию для консоли назначается кодовая страница OEM, но вы можете изменить её через SetConsoleCP, SetConsoleOutputCP для ввода/вывода соотвественно. Вопрос: Какого хрена так НЕ происходит?? (моя винда XP SP3) P.S: При выводе стандартными функциями наблюдается такое же поведение. Если кто знает причину, или, того лучше, знает как исправить это - пишите. Давайте раз и навсегда решим эту гадкую проблему кодировок (Unicode, CString и CharToOEM не предлагать)))!
Не знаю, чего там предлагать или нет, и чего ты хочешь в итоге - но OemToChar и CharToOem ЕДИНСТВЕННОЕ правильное решение. Если ты не хочешь использовать юникод, естественно. SetConsoleCP и смена шрифта консоли (да, есть и такое, недокументированное - позволяет сменить шрифт с терминала на люциду... правда, не по имени шрифта, а по номеру, который от системы к системе отличается) - не годятся, и даже потенциальная причина косяков на других машинах. Из-за того, что по умолчанию твоя консоль использует растровый шрифт типа Terminal, и вытекающую отсюда единственную ОЕМ-кодировку, вне зависимости от того, какую кодовую страницу ты выбираешь. Создай ярлык для запуска, пропиши в свойствах шрифта только Lucida Console - и узрей работающую смену кодировок...
Я тоже так думал. Но, открыл консольное приложение в C#, там по-умолчанию русский шрифт работает норм (видимо из-за Unicode) и потом посмотрел какой шрифт имеет данное консольное окно - это была не Lucida, когда врубил люсиду вместо русского шрифта отобразило кракозярбры. А как это реализовать через API?
setlocale действует только для функций из CRT (printf и т.п.). Если юзаешь только API, конвертируй сам.
Этот способ не работает. Компилятор говорит что такой функции знать не знает и знать не хочет( Идея с установкой шрифта действительно работает. Через функцию SetConsoleFont можно установить подходящий шрифт (эксперименты под моей ХР показали, что шрифтами с корректным отображением русских символов ANSI являются шрифты с номерами 6,7,10,11). Но работает это не ЧЕРЕЗ раз. Т.е. один раз запускаешь все норм, на следующий день ни один шрифт не работает. По-моему тут какое-то колдунство..
Варианты продолжение: 5) отказаться от консоли и юзать гуй. 6) написать свою версию консоли с расширенными возможностями и без стандартных болячек, кстати в сети несколько таких версий валяются (сейчас искать лень).
по крайней мере у меня работает: Код (Text): #include <locale> #include <iostream> int main() { setlocale(LC_ALL,"Russian"); std::cout << "Тест" << std::endl; system("pause"); return 0; }
SL7549 спасибо что подсказали где находятся объявление функции setlocale. Подключив locale компилятор перестал на неё ругаться, однако: Код (Text): setlocale(LC_ALL,".1251"); printf("Вася дурак\n"); и Код (Text): setlocale(LC_ALL,"Russian"); printf("Вася дурак\n"); упорно выводят на экран нечитабельную ахинею(.. Боюсь что придется переходить на Unicode. Жаль только что мой компилятор от gnu не умеет с ним работать (( на L"Text" ругаецо, а макрос _T("Text") не переводит текст в Unicode (при надписи wchar_t* str = _T("Text"); выдает cannot convert `const char*' to `wchar_t*' in initialization) Так и знал что freeware синоним слову гавно. *Пошел за комплятором от мелкомягких...
Пробовал. И #define UNICODE пробовал..На wchar_t* str = _T("Text"); ругаеца как и раньше. L"Text" не распознает (Illegal byte sequence).
Ты именно с чертой внизу _UNOCODE писал или просто UNICODE? Дело в том, что UNICODE - для поддержки юникода в WinAPI, а _UNICODE - для поддержки юникода в CRT. Причем #define _UNICODE должно находиться ДО заголовков. Короче вот работающий код в VC2003 Код (Text): #define _UNICODE #include <tchar.h> #include <locale> #include <iostream> int main() { wchar_t* str = _T("Кириллица"); _wsetlocale(LC_ALL,_T("Russian_Russia.866")); std::wcout << str << std::endl; system("pause"); return 0; }
Без форматирования Код (Text): void rus_txt_out(char *str) { char out_str[255]; CharToOem(str,out_str); printf("%s",out_str); return; } С форматированием Код (Text): void rus_printf(PCSTR Format, ...) { char sz_buffer[512]; char sz_outbuffer[512]; va_list va; va_start (va, Format); _vsnprintf(sz_buffer, sizeof(sz_buffer)-1, Format, va); va_end(va); CharToOem(sz_buffer,sz_outbuffer); printf(sz_outbuffer); return; }
Код (Text): Ты именно с чертой внизу _UNOCODE писал или просто UNICODE? Дело в том, что UNICODE - для поддержки юникода в WinAPI, а _UNICODE - для поддержки юникода в CRT. Причем #define _UNICODE должно находиться ДО заголовков. Да, я писал сначала _UNICODE. Результат тот же поэтому пока на это дело забил, до того как установлю компилятор от МС. Ув. RET, я же просил советы с CharToOem не предлагать, потому как это уже давно всем известный и через одно местовский метод.
Вот Си-листинг CharToOemA, полученный через HexRays: Код (Text): BOOL __stdcall CharToOemA(LPCSTR lpszSrc, LPSTR lpszDst) { LPCSTR v2; LPSTR v3; BOOL result; const CHAR v5; v2 = lpszSrc; if ( lpszSrc && (v3 = lpszDst, lpszDst) ) { do { *v3 = *(_BYTE *)(*v2 + dword_77FA1208 + 1210); v5 = *v2; ++v3; ++v2; } while ( v5 ); result = 1; } else { result = 0; } return result; } где dword_77FA1208, как я понимаю что-то связанное с локалью (можно в отладчике глянуть). При желании можно вставить уже готовый мелкософтовский код, что бы не был
Так, подведем итоги 2-го дня обсуждения. Итог 1: никто не знает как заставить консоль виндовс выводить текст в кодировке ANSI. Итог 2: Прогеры из микрософт - козлы. Впрочем тесты показали , что SetConsoleCP(1251); срабатывает, scanf, ReadConsole и даже ReadFile на буфер консольного ввода срабатывают на ура возвращая ANSI. В описании SetConsoleOutputCP однако написано что если стоит какой-то нетакой шрифт то она не будет иметь эффекта. Я обнаружил почему способ SetConsoleFont (,10); срабатывало через раз: если хоть раз за сессию (от вкл до выкл винды) вручную был установлен шрифт Lucida (даже если потом сразу заменен - для любого из консольных окон) то все работает. До этого - все 13 системных консольных шрифтов оторбражают кракозюли. Между прочим использование Unicode тоже не спасает, к примеру WriteConsoleW все равно выдает кракозюли для русского текста. Мб покопаться немного в кернеле IDой?)
В простейшем случае, сделать надстройку над принтф. Например myprintf. Которая сперва пропускает строку через CharToOEM, а потом передает ее оригинальной принтф. В более извращенной форме - похукать принтф.