【C语言】 Windows控制台API函数 及绘制表格

前言

在windows的控制台,也可以实现彩色的输出,以及简单的表格制作,有效的提高学习C语言的兴趣,可以用控制台开发一些简单的游戏,而无需学习其他的库。如下图,输出一个彩色的表格:
在这里插入图片描述
实现这些,只需要了解几个控制台的API函数就可以了。
了解控制台API函数,首先得了解 句柄 HANDL控制台API 结构体

1. 句柄 HANDLE

HANDLE 翻译为 手柄、把手。用在windows开发中,很形象。当抓住(获得)了一个设备的HANDLE(句柄) ,那么就基本可以控制这个设备了。
HANDLE 是一个无类型指针变量,那就是内存地址。在WinNT.h中定义

typedef void *HANDLE;

这里大概知道HANDLE的作用就行了,具体的可以查看句柄的百度百科

2. 控制台API结构体

这里只介绍本文中用到的结构体

2.1 坐标结构 COORD

定义 :定义控制台屏幕缓冲区中字符单元的坐标。 坐标系 (0,0) 的原点位于缓冲区的顶部左侧单元格。

typedef struct _COORD {
  SHORT X;  	//水平坐标或列值。 单位取决于函数调用。
  SHORT Y; 	//垂直坐标或行值。 单位取决于函数调用。
} COORD, *PCOORD;

2.2 光标信息结构 CONSOLE_CURSOR_INFO

typedef struct _CONSOLE_CURSOR_INFO {
  DWORD dwSize;			//光标的大小
  BOOL  bVisible;   //光标是否可以
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

2.3 控制台屏幕缓冲区信息结构 CONSOLE_SCREEN_BUFFER_INFO

typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
  COORD      dwSize;				//控制台屏幕缓冲区大小 ,这里区别于窗体大小,屏幕缓冲区包括滚动条拉动后能看到的区域
  COORD      dwCursorPosition;	 //屏幕中光标的坐标
  WORD       wAttributes;		//字符的属性(其实就是颜色代码,默认值为07,即黑色背景,白色字体)。 WORD是 unsigned short 类型。在32位系统中为16位长度,62位系统中未 32位
  SMALL_RECT srWindow;  //包含显示窗口左上角和右下角的控制台屏幕坐标的 SMALL_RECT 结构
  COORD      dwMaximumWindowSize; //包含控制台窗口的最大大小(以字符列和行为单位),给定当前屏幕缓冲区的大小和字体以及屏幕大小
} CONSOLE_SCREEN_BUFFER_INFO;

这个结构体的变量可以通过GetConsoleScreenBufferInfo函数获取,详情见3.5章节。

3. 控制台API函数

3.1 获取句柄 GetStdHandle

说明 : 检索指定标准设备的句柄(标准输入、标准输出或标准错误),
原型 :

HANDLE WINAPI GetStdHandle(_In_ DWORD nStdHandl);

返回值 :标准设备的句柄(即标准设备的在内存的映射地址)
参数:nStdHandle是DWORD类型的值,一共就三种

实际值含义
STD_INPUT_HANDLE-10标准输入设备,通常就是键盘
STD_OUTPUT_HANDLE-11标准输出设备 ,通常就是窗体、屏幕等
STD_ERROR_HANDLE-12标准错误设备,通常就是窗体、屏幕等

调用 :

HANDLE handle_output = GetStdHandle(STD_OUTPUT_HANDLE);// 这就获取了console窗体的句柄,此时对handle_output操作,就是对窗体的操作

3.2 获取光标信息 GetConsoleCursorInfo

说明 :获取光标信息
原型 :

BOOL WINAPI GetConsoleCursorInfo(
HANDLE               hConsoleOutput, 	//窗体句柄
PCONSOLE_CURSOR_INFO lpConsoleCursorInfo //光标信息指针,获取到的光标信息就存放在这个指针中
);

3.3 设置光标信息SetConsoleCursorInfo

说明 :设置光标的大小和可见性
原型 :

BOOL WINAPI SetConsoleCursorInfo( 
HANDLE hConsoleOutput, 	// 窗体的句柄
const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo //光标的结构信息
);

示例:

#include <stdio.h>
#include <Windows.h>
#include <conio.h>
int main()
{
	
	HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄
	CONSOLE_CURSOR_INFO cci;        //定义光标信息结构体
	GetConsoleCursorInfo(handle_out, &cci);     //获得当前光标信息
	printf("当前光标大小为%d:\n",cci.dwSize);

	printf("四秒后设置光标大小为1:\n");
	Sleep(4*1000);
	cci.dwSize = 1;     //设置光标尺寸为1
	SetConsoleCursorInfo(handle_out, &cci);
   
	printf("四秒后设置光标大小为50:\n");
	Sleep(4*1000);
	cci.dwSize = 50;    //设置光标尺寸为50
	SetConsoleCursorInfo(handle_out, &cci);

    
	printf("四秒后设置光标大小为100:\n");
	Sleep(4*1000);
	cci.dwSize = 100;   //设置光标尺寸为100
	SetConsoleCursorInfo(handle_out, &cci);
    
	printf("四秒后光标消失...");
	Sleep(4*1000);
	cci.bVisible = 0;       //设置光标为不可见
	SetConsoleCursorInfo(handle_out, &cci);
	getchar();
	return 0;
}

在这里插入图片描述## 设置

3.4 设置光标位置 SetConsoleCursorPosition

说明 :设置光标在窗体的位置。
   有了这个函数,就不必一行一行的输出了,可以自由的定位到窗体的任何位置。这个函数在开发控制台小游戏时,总是会用到。
原型:

BOOL WINAPI SetConsoleCursorPosition(
HANDLE hConsoleOutput,		//窗体句柄
COORD  dwCursorPosition		//设置的光标坐标
);

示例:

#include <stdio.h>
#include <Windows.h>
#include <conio.h>

/*设置光标位置*/
void SetConsoleCursorPositionTest(int x, int y)
{
     COORD pos = {x, y};/* x是横坐标 y是纵坐标 */
      HANDLE handle_output = GetStdHandle(STD_OUTPUT_HANDLE);
     SetConsoleCursorPosition(handle_output,pos);        /* 设置控制台光标坐标(设备句柄, 光标坐标) */
	 printf("光标到这里了");
}

int main()
{
	SetConsoleCursorPositionTest(23, 2);
	getchar();
	return 0;
}

测试结果 :
在这里插入图片描述

3.5 获取屏幕缓冲区的信息 GetConsoleScreenBufferInfo

说明:获取屏幕缓冲区的信息
区别于窗体,屏幕缓冲区,包括滚动条拉动能看到的区域
原型 :

BOOL WINAPI GetConsoleScreenBufferInfo(
	HANDLE                      hConsoleOutput, //控制台句柄
	PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo //屏幕缓冲区信息指针
);

示例:

#include <stdio.h>
#include <Windows.h>
#include <conio.h>

//测试 获取控制台屏幕缓冲区信息GetConsoleScreenBufferInfo()函数
void GetConsoleScreenBufferInfoTest ()
{
    CONSOLE_SCREEN_BUFFER_INFO scr;
    HANDLE handle_output = GetStdHandle(STD_OUTPUT_HANDLE);    /* 获取标准输出句柄 */
    GetConsoleScreenBufferInfo(handle_output, &scr);
    printf("\n当前光标坐标:x=%d  y=%d\n\n", scr.dwCursorPosition.X, scr.dwCursorPosition.Y);
    printf("窗体最大:宽=%d,高=%d\n\n",scr.dwMaximumWindowSize.X, scr.dwMaximumWindowSize.Y);
    printf("屏幕缓冲区: 宽=%d  高=%d\n\n",scr.dwSize.X, scr.dwSize.Y);//这里的高的值很大,因为我们肉眼见到的只是窗体的高,
																    //不是屏幕缓冲区的高,拉动滚动条能够看到的区域才是
    printf("窗体左上角坐标: x=%d y=%d\n\n", scr.srWindow.Left,scr.srWindow.Top);
	printf("窗体右下角坐标: x=%d y=%d\n\n", scr.srWindow.Right,scr.srWindow.Bottom );
    printf("字符属性:%d\n",scr.wAttributes);  //字符的属性(颜色代码)
    
}

int main()
{
	GetConsoleScreenBufferInfoTest ();
	getchar();
	return 0;
}

示例结果:
在这里插入图片描述

3.6 设置文本属性 SetConsoleTextAttribute

说明: 设置控制台文本属性(颜色),可以设置前景色FOREGROUND(文本颜色)和背景色BACKGROUND。有了这个控制台 就不在是黑底白字了,那么单调了。
原型 :

BOOL WINAPI SetConsoleTextAttribute(
  _In_ HANDLE hConsoleOutput,	//控制台句柄
  _In_ WORD   wAttributes		  //无符号短整型的颜色码
);

c语言只有16种颜色,颜色码如下:

    0=黑色    8=灰色  
   1=蓝色    9=淡蓝色           
  2=绿色    10=淡绿色  0xa    
  3=湖蓝色  11=淡浅绿色  0xb 
  4=红色    12=淡红色  0xc  
  5=紫色    13=淡紫色  0xd    
  6=黄色    14=淡黄色  0xe    
  7=白色    15=亮白色  0xf 

第二个参数 wAttributes(颜色属性)使用方式有三种:
a. 使用十六进制方式
因为wAttributes是WORD类型(unsigned short)所以为16位,如0x16。其中高阶位为背景色
低阶位为前景色(字体颜色)

 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x16);//这里的0x16中的1代表蓝色背景色,6代表金色前景色)

可以参照以下的16进制颜色对照表
在这里插入图片描述b. 使用10进制方式

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 26);

在这里插入图片描述3. 使用宏定义方式

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_GREEN|FOREGROUND_BLUE);//使用绿色背景色和蓝色前景色

常用的宏定义的如下:

#define FOREGROUND_BLUE      0x0001 // text color contains blue.
#define FOREGROUND_GREEN     0x0002 // text color contains green.
#define FOREGROUND_RED       0x0004 // text color contains red.
#define FOREGROUND_INTENSITY 0x0008 // text color is intensified.
#define BACKGROUND_BLUE      0x0010 // background color contains blue.
#define BACKGROUND_GREEN     0x0020 // background color contains green.
#define BACKGROUND_RED       0x0040 // background color contains red.
#define BACKGROUND_INTENSITY 0x0080 // background color is intensified.
#define COMMON_LVB_LEADING_BYTE    0x0100 // Leading Byte of DBCS 
#define COMMON_LVB_TRAILING_BYTE   0x0200 // Trailing Byte of DBCS
#define COMMON_LVB_GRID_HORIZONTAL 0x0400 // DBCS: Grid attribute: top horizontal.
#define COMMON_LVB_GRID_LVERTICAL  0x0800 // DBCS: Grid attribute: left vertical.
#define COMMON_LVB_GRID_RVERTICAL  0x1000 // DBCS: Grid attribute: right vertical.
#define COMMON_LVB_REVERSE_VIDEO   0x4000 // DBCS: Reverse fore/back ground attribute.
#define COMMON_LVB_UNDERSCORE      0x8000 // DBCS: Underscore.

基础色
在这里插入图片描述

以COMMON开头的宏变量,只有在64位系统下才能生效。因为64位系统,WORD是32位的。

示例:


#include <stdio.h>
#include <Windows.h>
#include <conio.h>

//设置控制台文本属性(颜色)
void SetConsoleTextAttributeTest()
{
	HANDLE handle_output = GetStdHandle(STD_OUTPUT_HANDLE);    /* 获取标准输出句柄 */
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x16);//这里的0x16中的1代表蓝色背景色,6代表金色前景色)
	printf("\n方式1,十六进制方式: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x16) \n");

	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 26);
	printf("\n方式2,十进制方式:  SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 26); \n");
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_GREEN|FOREGROUND_RED);

	printf("\n方式3,宏定义方式: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_GREEN|FOREGROUND_RED)");

	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
	printf("\n\n我是红色字体");
}

int main()
{
	GetConsoleScreenBufferInfoTest ();
	getchar();
	return 0;
}

在这里插入图片描述

3.7 设置标题 SetConsoleTitle

说明 :设置当前控制台窗口的标题。
原型:

BOOL WINAPI SetConsoleTitle(
  _In_ LPCTSTR lpConsoleTitle
);

示例:

#include <stdio.h>
#include <Windows.h>
#include <conio.h>

//设置控制台文本属性(颜色)
void SetConsoleTitleTest()
{
	HANDLE handle_output = GetStdHandle(STD_OUTPUT_HANDLE);    /* 获取标准输出句柄 */
	SetConsoleTitle("控制台API练习V1.0"); 
}

int main()
{
	GetConsoleScreenBufferInfoTest (); //使用的是3.6章节的测试
	SetConsoleTitleTest();
	getchar();
	return 0;
}

在这里插入图片描述

4. 绘制彩色表格

在控制台中绘制一个表格,表格的大小可以修改,可以定位到表格的任意个位置。

4.1 表格结构类型 Grid_t

//定义一个表格
typedef struct grid_s{
	COORD startPos;  //左上角坐标
	COORD size;	    // 表格的长度和宽度
} Grid_t;

4.2 绘制表格

主要分为三部分:绘制表格的顶部、中间部分、底部

// 绘制表格
void DrawGrid(HANDLE handle_out,Grid_t grid){
	
	
	int i,j,k;
	int realRowLen;		//实际画表格的行数; 第0行 - 第realRowLen行
	int realColLen;		//实际画表格的列数; 第0列 - 第realColLen列
	char * top[4] = { "┌", "—", "┬", "┐" };		//顶部的4个绘表符号
    char * center_data[2] = { "│", "  " };			//表格中间的行(输出数据的行)
    char * center[4] = { "├", "—", "┼", "┤" };	//表格中间的行 (数据行分隔的行);
    char * bottom[4] = { "└", "—", "┴", "┘" };	//底部的4个绘表符号
   
	realRowLen = 2 * grid.size.Y + 1;
	realColLen = 2 * grid.size.X + 1;
	SetConsoleCursorPosition(handle_out,grid.startPos);        /* 设置控制台光标坐标(设备句柄, 光标坐标) */

	//输出顶部字符
    printf(top[0]);								//最左边的边框 "┌"
    for (i = 1; i < realColLen-1; i++)
    {
		if (i % 2 == 1)
			printf(top[1]);						//奇数列,输出 "─"
		else
		    printf(top[2]);						//偶数列,输出 "┬"

    }
    printf("%s\n",top[3] );						//最右边的边框 "┐"


	//输出表格中间的部分
	for(i = 1;	i < realRowLen-1; i++)
	{
		for(k =0 ;k < grid.startPos.X ; k++)
		{
			printf(" ");
		}
		if( i % 2 == 1)							//相当于if(i=1,3,5,7...2*row-3)单数行,即带有数据的行
		{
			for(j = 0;j < realColLen ; j++)	
			{
				if( j % 2 == 0)
					printf(center_data[0]);		//双数列,  输出 "|"
				else
					printf(center_data[1]);		//单数列为空 ,输出 " "	
			}
			printf("\n");
		}
		else									//相当于  if(i=0,i=2...) 双数行 即分隔行
		{
			printf(center[0]);					//最左边的边框 "├"
			for(j = 1;j < realColLen-1 ; j++)	
			{
				if( j % 2 == 1)
					printf(center[1]);			//奇数列,输出 "─" 
				else
					printf(center[2]);			//偶数列,输出 "┼"
			}
			 printf("%s\n",center[3] );			//最右边的边框 "┐"
		}
	}


	 //输出底部字符

	for(k =0 ;k < grid.startPos.X ; k++)
	{
			printf(" ");
	}

	printf(bottom[0]);							//最左边框 "└"
    for (i = 1; i <realColLen-1; i++)
    {
            if (i % 2 == 1)					
				printf("%s",bottom[1]);			//奇数列 "─"
            else							
                printf("%s",bottom[2]);			//偶数列 "┴"
    }
    printf("%s\n",bottom[3] );					//最右边框 "┘"

}

4.3 定位表格位置

//定位表格的位置
void LocateGridPos(HANDLE handle_out,Grid_t grid,int x,int y)
{

 COORD pos={3*x+2, 2*y+1};
 pos.X += grid.startPos.X;
 pos.Y += grid.startPos.Y;
 SetConsoleCursorPosition(handle_out,pos);        /* 设置控制台光标坐标(设备句柄, 光标坐标) */

}

4.4 在表格的固定位置打印字符

void GridCellPrintChar(HANDLE handle_out,Grid_t grid ,int x,int y, char  cellValue)
{
	LocateGridPos(handle_out,grid, x, y); //先定位
	printf("%c",cellValue); //打印

}

4.5 完整测试代码


#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>

typedef struct grid_s{
	COORD startPos;  //左上角坐标
	COORD size;	    // 表格的长度和宽度
} Grid_t;

// 绘制表格
void DrawGrid(HANDLE handle_out,Grid_t grid){
	
	
	int i,j,k;
	int realRowLen;		//实际画表格的行数; 第0行 - 第realRowLen行
	int realColLen;		//实际画表格的列数; 第0列 - 第realColLen列
	char * top[4] = { "┌", "—", "┬", "┐" };		//顶部的4个绘表符号
    char * center_data[2] = { "│", "  " };			//表格中间的行(输出数据的行)
    char * center[4] = { "├", "—", "┼", "┤" };	//表格中间的行 (数据行分隔的行);
    char * bottom[4] = { "└", "—", "┴", "┘" };	//底部的4个绘表符号
    

	
	realRowLen = 2 * grid.size.Y + 1;
	realColLen = 2 * grid.size.X + 1;
	
	SetConsoleCursorPosition(handle_out,grid.startPos);        /* 设置控制台光标坐标(设备句柄, 光标坐标) */

	//输出顶部字符
    printf(top[0]);								//最左边的边框 "┌"
    for (i = 1; i < realColLen-1; i++)
    {
		if (i % 2 == 1)
			printf(top[1]);						//奇数列,输出 "─"
		else
		    printf(top[2]);						//偶数列,输出 "┬"

    }
    printf("%s\n",top[3] );						//最右边的边框 "┐"


	//输出表格中间的部分
	for(i = 1;	i < realRowLen-1; i++)
	{
		for(k =0 ;k < grid.startPos.X ; k++)
		{
			printf(" ");
		}
		if( i % 2 == 1)							//相当于if(i=1,3,5,7...2*row-3)单数行,即带有数据的行
		{
			for(j = 0;j < realColLen ; j++)	
			{
				if( j % 2 == 0)
					printf(center_data[0]);		//双数列,  输出 "|"
				else
					printf(center_data[1]);		//单数列为空 ,输出 " "	
			}
			printf("\n");
		}
		else									//相当于  if(i=0,i=2...) 双数行 即分隔行
		{
			printf(center[0]);					//最左边的边框 "├"
			for(j = 1;j < realColLen-1 ; j++)	
			{
				if( j % 2 == 1)
					printf(center[1]);			//奇数列,输出 "─" 
				else
					printf(center[2]);			//偶数列,输出 "┼"
			}
			 printf("%s\n",center[3] );			//最右边的边框 "┐"
		}
	}


	 //输出底部字符

	for(k =0 ;k < grid.startPos.X ; k++)
	{
			printf(" ");
	}

	printf(bottom[0]);							//最左边框 "└"
    for (i = 1; i <realColLen-1; i++)
    {
            if (i % 2 == 1)					
				printf("%s",bottom[1]);			//奇数列 "─"
            else							
                printf("%s",bottom[2]);			//偶数列 "┴"
    }
    printf("%s\n",bottom[3] );					//最右边框 "┘"

}

//定位表格的位置
void LocateGridPos(HANDLE handle_out,Grid_t grid,int x,int y)
{	

	 COORD pos={3*x+2, 2*y+1};
	 pos.X += grid.startPos.X;
	 pos.Y += grid.startPos.Y;
     SetConsoleCursorPosition(handle_out,pos);        /* 设置控制台光标坐标(设备句柄, 光标坐标) */

}

void GridCellPrintChar(HANDLE handle_out,Grid_t grid ,int x,int y, char  cellValue)
{
	LocateGridPos(handle_out,grid, x, y);
	
	printf("%c",cellValue);

}

int main()
{

	int i,j;
	Grid_t grid={ {3,3}, {5,5}};			//定义一个表格起始位置为{3,3},长度为,宽度为5;

	int gridData [5][5]={ {1,1,1,1,0},      //定义一个5*5的数组,用于判断表格中是否输出"a"
						  {0,0,0,1,0},
						  {0,1,0,1,0},
						  {0,0,1,1,0},
						  {0,0,1,0,1}};

	HANDLE handle_output = GetStdHandle(STD_OUTPUT_HANDLE);   //获取标准输出句柄
	SetConsoleTextAttribute(handle_output, FOREGROUND_GREEN); //将输出修改成绿色,这样表格就画出绿色的了

	DrawGrid(handle_output,grid);							  //画表格
	SetConsoleTextAttribute(handle_output, FOREGROUND_RED);	  //将输出修改成红色
	
	for(j = 0; j < 5; j++)									  //在表格的指定位置赋值
	{
		for(i = 0;i < 5; i++)
		if(gridData[j][i]==1){
			LocateGridPos(handle_output,grid,i,j);
			printf("a");
		}
	}
    SetConsoleTextAttribute(handle_output, 0xe);
	GridCellPrintChar(handle_output,grid ,1,4, 'z');         
	CloseHandle(handle_output);								 
	getchar();
	return 0;

}
  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值