CodeNet / Языки программирования / C / C++ / Microsoft Visual C++ / Работа с текстом и строками
Возможности консольных приложений для Windows
27 апреля 2007 года
В настоящее время, когда у разработчиков программного обеспечения появляется всё больше возможностей для сознания приложений со сложным графическим интерфейсом, консольные приложения по-прежнему прочно удерживают свои позиции, даже в такой, казалось бы им среде, как современная операционная система Windows. Это объясняется в первую очередь простотой их исполнения и некоторыми специфическими особенностями консольных приложений, которые делают их в ряде случаев более подходящими для решения задачи, чем приложения с графическим интерфейсом. Один минус: выглядят такие приложения очень уныло и однообразно, как безликая серая масса букв и цифр на чёрном фоне окна консоли. Но не всё так безнадёжно, как может показаться на первый взгляд. В этой статье я попытаюсь дать несколько полезных рецептов расширения функциональности консольных приложений и придания их внешнему виду большей выразительности.
И так: начнём с заголовка окна консоли. При запуске программы в заголовке окна отображается полное имя файла, с которого было запущено приложение. Вместо имени файла, в заголовке окна можно указать любой другой текст: название приложения, например. Это делается с помощью функции SetConsoleTitle.
::SetConsoleTitle(_T("Пример программы на C++"));
Далее рассмотрим проблему с выводом на консоль текста кириллицы. Практически каждому программисту когда-нибудь приходилось с этим сталкиваться. По сложившейся традиции, для вывода текста на консоль в большинстве примеров на C++ используются функции стандартной библиотеки, такие как printf или puts, которые работают с текстом в кодировке OEM, что соответствует кодовой странице 866 для русского языка. То же самое происходит при использовании потока вывода cout. Но проблема в том, что большинство текстовых редакторов для Windows работают с текстом в кодировке ANSI, что для русского языка соответствует кодовой странице 1251.
Как решить эту проблему? Проще всего использовать функции, которые записывают текст непосредственно в буфер консоли, а не в стандартный поток. Для этого нужно в программный код включить заголовочный файл CONIO.H и, вместо функций printf и puts, вызывать аналогичные функции _cprintf и _cputs.
Так они выглядят в коде на Visual C++:
_cputts(_T("Всем привет от меня!\r\n")); _tcprintf(_T("%s\r\n"), _T("Благодарю за внимание."));
Несложно написать и собственную процедуру вывода текста на консоль с использованием системной функции WriteConsole. Вот пример такой процедуры:
BOOL PrintText(LPCTSTR szText) { static HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); // DWORD dw(0); return ::WriteConsole(hConsole, szText, ::lstrlen(szText), &dw, NULL); }
А вот так она используется:
PrintText(_T("Всем пока...\r\n"));
Как вы уже наверно заметили, для перевода строки необходимо указывать последовательность из двух символов: "\r\n", в отличие от стандартного потока, где указывается лишь один символ '\n'. Но бывает и так, что нужно вывести текст именно через стандартный поток, чтобы его можно было перенаправить в текстовый файл или в другое консольное приложение. Для этого текст нужно перевести в другую кодировку с помощью функции CharToOem. Или использовать функцию WideCharToMultiByte с параметром CP_OEMCP, если ваш текст в кодировке UNICODE.
Размер буфера консоли по умолчанию равен 80х300, т.е. 300 строк по 80 символов каждая. Задать буферу консоли другой размер можно при помощи функции SetConsoleScreenBufferSize.
Атрибутами текста консоли являются: цвет символов и цвет заднего фона. Код атрибута соответствует числовому значению в диапазоне от 0x00 до 0x7F. Так выглядит таблица с числовыми значениями атрибутов текста в шестнадцатеричном коде:
00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1A | 1B | 1C | 1D | 1E | 1F |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 2A | 2B | 2C | 2D | 2E | 2F |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 3A | 3B | 3C | 3D | 3E | 3F |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 4A | 4B | 4C | 4D | 4E | 4F |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 5A | 5B | 5C | 5D | 5E | 5F |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 6A | 6B | 6C | 6D | 6E | 6F |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 7A | 7B | 7C | 7D | 7E | 7F |
Функция SetConsoleTextAttribute задаёт выводимому тексту указанный атрибут. Для задания всему окну консоли указанных атрибутов применяется функция FillConsoleOutputAttribute.
Позиционирование текста (т.е. установка курсора на позицию с заданными координатами) производится с помощью функции SetConsoleCursorPosition.
Полный код моего примера в Visual C++ 2005 выглядит так:
#include "stdafx.h" #include <conio.h> BOOL PrintText(LPCTSTR szText); BOOL SetConsoleAttrib(WORD wAttrib); BOOL SetCurrentPos(SHORT x, SHORT y); BOOL SetConsoleSize(SHORT x, SHORT y); int _tmain(int argc, _TCHAR* argv[]) { ::SetConsoleTitle(_T("Пример программы на C++")); // SetConsoleSize(160, 80); SetConsoleAttrib(0x6F); // _cputts(_T("Всем привет от меня!\r\n")); _tcprintf(_T("%s\r\n"), _T("Благодарю за внимание.")); // SetCurrentPos(25, 10); PrintText(_T("Всем пока...\r\n")); // _gettch(); // ожидание нажатия клавиши... return 0; } static HANDLE _ConsoleOut = ::GetStdHandle(STD_OUTPUT_HANDLE); BOOL SetConsoleAttrib(WORD wAttrib) { ::SetConsoleTextAttribute(_ConsoleOut, wAttrib); CONSOLE_SCREEN_BUFFER_INFO csbi = {0}; ::GetConsoleScreenBufferInfo(_ConsoleOut, &csbi); DWORD dw(0); COORD cr = {0, 0}; return ::FillConsoleOutputAttribute(_ConsoleOut, wAttrib, csbi.dwSize.X * csbi.dwSize.Y, cr, &dw); } BOOL PrintText(LPCTSTR szText) { DWORD dw(0); return ::WriteConsole(_ConsoleOut, szText, ::lstrlen(szText), &dw, NULL); } BOOL SetCurrentPos(SHORT x, SHORT y) { COORD pos = {x, y}; return ::SetConsoleCursorPosition(_ConsoleOut, pos); } BOOL SetConsoleSize(SHORT x, SHORT y) { COORD size = {x, y}; return ::SetConsoleScreenBufferSize(_ConsoleOut, size); }
Приведённый пример был создан в среде Visual C++ 2005 как проект Win32 Console Application.
В заключение хочу отметить, что возможности консольного интерфейса Windows отнюдь не исчерпываются теми функциями, которые были упомянуты в этой статье. Не было рассказано про получение и обработку сообщений от мыши, об использовании в консольных приложениях возможностей графического интерфейса Windows. Но об этом речь пойдёт в дальнейших публикациях...
Благодарю за внимание.