控制台配置相关

设置控制台代码页为UTF-8

简中版Windows下控制台默认代码页为936(GBK), 如果直接显示UTF8内容会乱码, 此时需要用以下代码将代码页修改到兼容UTF-8的代码页.

首先如果你使用msvc开发应用,必须确保启用编译器的utf8模式

  • 如果你使用Visual Studio项目进行构建, 你需要在项目属性中找到Configuration Properties > C/C++ > Command Line, 在Additional Options中添加/utf-8参数(详见此文档

  • 如果你正在使用cmake进行构建,添加下面这段代码到CMakeList.txt

1
2
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")

此时编译器将使用UTF8模式编译应用.

接下来在Main函数开头执行下列之一函数:

Method1: 使用std::setlocale

1
2
3
4
5
6
#include <clocale>

// ...
void setConsoleUTF8() {
std::setlocale(LC_ALL, "chs.utf8");
}

Method2: 使用Windows.h中的native方法

(实测此方法在输出效率上会优于Method1 (待验证))

1
2
3
4
5
6
7
8
9
10
11
12
#include <Windows.h>
#include <codecvt>

// ...
void setConsoleUTF8() {
SetConsoleOutputCP(CP_UTF8);
#pragma warning (disable: 4996)
std::locale utf8(std::locale(), new std::codecvt_utf8_utf16<wchar_t>);
std::wcout.imbue(utf8);
#pragma warning (default: 4996)

}

启用虚拟终端(VT Mode)

启用虚拟终端模式后可以在Windows中使用在Linux/Mac中可用的转义字符序列控制终端行为
经测试虚拟终端功能仅支持win10及更新的系统.
可参考相关文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <Windows.h>

bool EnableVTMode()
{
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
{
return false;
}

DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode))
{
return false;
}

dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(hOut, dwMode))
{
return false;
}
return true;
}

控制台输出相关

获取/修改光标位置信息

使用以下方法可以快速获取/修改光标位置,或者按照相对距离移动光标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <Windows.h>

void setCursorPos(int x, int y)
{
HANDLE hOut;
COORD Position;

hOut = GetStdHandle(STD_OUTPUT_HANDLE);

Position.X = x;
Position.Y = y;

SetConsoleCursorPosition(hOut, Position);

}

COORD getCursorPos()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE hOut;
COORD Position;

hOut = GetStdHandle(STD_OUTPUT_HANDLE);

GetConsoleScreenBufferInfo(hOut, &csbi);

Position.X = csbi.dwCursorPosition.X;
Position.Y = csbi.dwCursorPosition.Y;

return Position;
}

void moveCursorPos(int vecX, int vecY)
{
auto initPos = getCursorPos();
HANDLE hOut;
COORD Position;

hOut = GetStdHandle(STD_OUTPUT_HANDLE);

Position.X = initPos.X + vecX;
Position.Y = initPos.Y + vecY;

SetConsoleCursorPosition(hOut, Position);
}


修改输出字符颜色

修改字符颜色建议使用VT模式完成
下面代码首先需要启用VT模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>

const char ESC = '\033';

const int ATTR_NORMAL = 0; // 传入此常量可将当前颜色设置恢复为预设值

//前景色
enum ConsoleForeground : int
{
FG_BLACK = 30,
FG_RED = 31,
FG_GREEN = 32,
FG_YELLOW = 33,
FG_BLUE = 34,
FG_MAGENTA = 35,
FG_CYAN = 36,
FG_WHITE = 37,
FG_DEFAULT = 39,
FG_LIGHT_BLACK = 90,
FG_LIGHT_RED = 91,
FG_LIGHT_GREEN = 92,
FG_LIGHT_YELLOW = 93,
FG_LIGHT_BLUE = 94,
FG_LIGHT_MAGENTA = 95,
FG_LIGHT_CYAN = 96,
FG_LIGHT_WHITE = 97
};

// 背景色
enum ConsoleBackground : int
{
BG_BLACK = 40,
BG_RED = 41,
BG_GREEN = 42,
BG_YELLOW = 43,
BG_BLUE = 44,
BG_MAGENTA = 45,
BG_CYAN = 46,
BG_WHITE = 47,
BG_DEFAULT = 49,
BG_LIGHT_BLACK = 100,
BG_LIGHT_RED = 101,
BG_LIGHT_GREEN = 102,
BG_LIGHT_YELLOW = 103,
BG_LIGHT_BLUE = 104,
BG_LIGHT_MAGENTA = 105,
BG_LIGHT_CYAN = 106,
BG_LIGHT_WHITE = 107
};

void setColorAttr(int attr)
{
std::cout << ESC << "[" << attr << "m";
}

除此之外,部分终端还支持设置自定义的RGB颜色
详见Microsoft docs

清空控制台屏幕

Method1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void clearScreen()
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coordScreen = {0, 0}; // home for the cursor
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;


// Get the number of character cells in the current buffer.
if( !GetConsoleScreenBufferInfo(hConsole, &csbi ))
{
return;
}

dwConSize = csbi.dwSize.X * csbi.dwSize.Y;

// Fill the entire screen with blanks.

if( !FillConsoleOutputCharacter(hConsole, // Handle to console screen buffer
(TCHAR) ' ', // Character to write to the buffer
dwConSize, // Number of cells to write
coordScreen, // Coordinates of first cell
&cCharsWritten ))// Receive number of characters written
{
return;
}

// Get the current text attribute.

if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
{
return;
}

// Set the buffer's attributes accordingly.

if( !FillConsoleOutputAttribute(hConsole, // Handle to console screen buffer
csbi.wAttributes, // Character attributes to use
dwConSize, // Number of cells to set attribute
coordScreen, // Coordinates of first cell
&cCharsWritten )) // Receive number of characters written
{
return;
}

// Put the cursor at its home coordinates.

SetConsoleCursorPosition( hConsole, coordScreen );
}

Method2: 需启用VT模式

此方法执行效率略低于Method1(待验证)

1
2
3
4
void clearScreen()
{
std::cout << "\033[H\033[2J\033[3J";
}

处理键盘输入相关

使用io库操作标准输入时, 用户的输入都会被存储在缓冲区
如需直接对键盘输入进行处理, 可以使用conio.h中的_getch()_kbhit()函数

_getch() 可用于获取键盘输入的一个字符(不会回显), 当缓冲区内没有待处理的键盘输入时, 将阻塞程序

_kbhit() 可用于判断键盘是否有输入, 若缓冲区内没有待处理的键盘输入时, 将返回false

一个典型的键盘处理方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

// 本方法不会阻塞线程
void InputService::tickInput()
{
while (_kbhit())
{
int key = _getch();
if (key == 0xE0 || key == 0)
{
// 处理功能键或方向键
key = _getch();
switch (key)
{
case 72:
// Up arrow
break;
case 80:
// Down arrow
break;
case 75:
// Left arrow
break;
case 77:
// Right arrow
break;
// 如果需要处理更多的功能键,可在此处添加
default:
break;
}
}
else
{
// 在此处处理按键输入,输入按键即为对应字符
// 特别的, Enter键key为0x0D('\r'), Backspace键为0x08, Esc键为0x1B, Tab键为0x09
}
}
}