C语言与VT100控制码编程

声明:
. 如果您打算阅读本文,希望您已经了解过C语言的基本语法,本文不对C语言的基本语法进行说明,因为那些东西几乎唾手可得;
. 本文在vim中编辑,请尽量是用vim进行阅读,因为有不对齐的现象;
. 本人强烈建议您先编译,运行本文最后提供的sinDemo源代码,再看本文的正文,因为您看了运行效果,您就知道本人为什么要写这篇文章; \\\\\\\\\\\\--*目录*--///////////
| 一. 需求背景 |
| 二. VT100控制码是什么 |
| 三. sin函数动态图sinDemo示例 |
\\\\\\\\\\\\\\\\\//////////////// 一. 需求背景
以前很长时间里,打开Ubuntu的终端,使用会产生动态效果的shell命令(如top)让我觉得不可思议,于是很多时候也希望自己的程序也能那样动起来,但由于自己的知识面的原因,不知道的东西太多:
. 如何改变字符输出的位置?
. 如何改变前景色,背景色?
. 最重要的是,查资料时用什么关键字查也不知道?
当然,也许有人会说,去问人,可问题是我也不知道怎么去描述我的需求,更不知道谁知道这玩意.那时候觉这是一件挺难的事,于是我开始凭着感觉是用不同的关键字百度,最后是通过python找到tput,然后通过tput找到VT100码,因为时间已经过去挺久了,具体的经过也基本上忘记了. :)
在使用了一段时间的VT100码以后,我发现,我们学C语言的时候,就应该学会配合使用VT100码,因为这样你可以在黑白的终端世界里做出很酷的东西,比如俄罗斯方块,贪吃蛇等等,还有其他的一些经典的动画效果,个人觉得早期开发游戏的那些人,也许就是这么干的,本文的Demo提供了一个生成sin函数的动画效果.
本人也使用VT100码实现了在终端绘制方框,填充方框,使用不同的字符绘制直线,等等内容,并且把这些做成了一个库供自己使用.其实shell命令里的tput也是这么做,Ncurses的底层也是这么干,只不过貌似他们做得比我好,考虑得更周到. :) 二. VT100控制码是什么
VT100是一个终端类型定义,VT100控制码是用来在终端扩展显示的代码.比如在终端上任意坐标用不同的颜色显示字符.所有的控制符是'\033'(033是八进制的数,十进制对应的是27,即ESC的ASCII码,如果需要查看,可以使用shell命令:man ascii)开头.用输出字符语句来输出,在C程序中用printf来输出VT100的控制字符.
. VT100 控制码归类如下。
\[0m 取消之前所有属性
\[1m 设置高亮度
\[4m 下划线
\[5m 闪烁
\[7m 反显
\[8m 消隐
\[30m -- \[37m 设置前景色 |------------+
\[40m -- \[47m 设置背景色 |------------+
\[nA 光标上移 n 行 |
\[nB 光标下移 n 行 |
\[nC 光标右移 n 行 |
\[nD 光标左移 n 行 |
\[y;xH 设置光标位置 |
\[2J 清屏 |
\[K 清除从光标到行尾的内容 |
\[s 保存光标位置 |
\[u 恢复光标位置 |
\[?25l 隐藏光标 |
\[?25h 显示光标 |
V
+------<----------------------------<--+
|
V
. VT100 的颜色输出分为,前景色和背景色可以分别输出,如果不需要之前所有的设置可以用\[0m取消。
. 字背景颜色范围:----
:黑
:深红
:绿
:黄色
:蓝色
:紫色
:深绿
:白色
. 字前景颜色范围:----
:黑
:红
:绿
:黄
:蓝色
:紫色
:深绿
:白色
. 输出一个字符串( something here )有前景色和背景色代码如下:
printf("\033[41;36m something here \033[0m"); 三. sin函数动态图sinDemo示例
. 示例终端输出一屏图像:
| SinDemo | --------------------+------@--------------> Y
|----------*
|---------------*
|------------------*
|------------------*
|------------------*
|---------------*
|----------*
|-----*
*
*-----|
*----------|
*---------------|
*------------------|
*------------------|
*------------------|
*---------------|
*----------|
*-----|
*
V X
. sinDemo源代码:
 /******************************************************************
* sinDemo
*
* 1. 本Demo主要目标是为了实现在终端下实现sin函数的动态效果;
* 2. 本Demo之前是使用shell tput和C语言实现的,这次将其改为
* C语言+VT100控制码的形式;
*
* 2015-3-27 阴 深圳 曾剑峰
*
*****************************************************************/
#include <stdio.h>
#include <math.h>
#include <unistd.h> /**
* 定义圆周率的值
*/
#define PI 3.14
/**
* 本Demo中假设sin曲线周期为20,幅值也是20,幅值分正负幅值,
* 所以后面的很多地方有SIN_AMPLITUDE*2,也就是Y轴方向上的值.
*/
#define SIN_AMPLITUDE 20
/**
* 定义每次刷新图形时间间隔为100ms
*/
#define DELAY_TIME 100000
/**
* 定义圆的一周角度为360度
*/
#define TRIANGLE 360.0
/**
* 输出的时候,数字行放在哪一行,也就是输出图形中的这行数字:
* 0123456789012345678901234567890123456789
* 本Demo中把上面这行数字放在界面的第3行
*/
#define Y_NUMBER_BEGIN_LINE 3
/**
* 在本Demo中,图形就在上面数字行的下一行,也就是输出图形中如下面的内容:
* 0000 --------------------@--------------------> Y
* 0001 |-----*
* 0002 |----------*
* 0003 |---------------*
* 0004 |------------------*
* 0005 |------------------*
* 0006 |------------------*
* 0007 |---------------*
* 0008 |----------*
* 0009 |-----*
* 0010 *
* 0011 *-----|
* 0012 *----------|
* 0013 *---------------|
* 0014 *------------------|
* 0015 *------------------|
* 0016 *------------------|
* 0017 *---------------|
* 0018 *----------|
* 0019 *-----|
* 0020 V X
*/
#define SIN_GRAPH_BEGIN_LINE (Y_NUMBER_BEGIN_LINE+1) int main(int argc, char* argv[]){ /**
* 局部变量说明:
* 1. i : 主要用于循环计算;
* 2. lineNumber : 用于保存行号;
* 3. offsetCenter : 用于保存sin曲线上的点的相对于中心轴的偏移;
* 4. nextInitAngle : 保存下一屏要输出图形的初始角度制角度(如30度);
* 5. currentInitAngle : 当前一屏要输出的图形的初始角度制角度(如30度);
* 6. currentInitradian : 当前一屏要输出的图形的初始弧度制弧度(如PI/6)
* 根据currentInitAngle换算而来,因为sin函数需要
* 角度制进行求值;
*
*/
int i = ;
int lineNumber = ;
int offsetCenter = ;
int nextInitAngle = ;
double currentInitAngle = ;
double currentInitradian = ; //软件开始运行,清一次屏,保证屏幕上没有无关内容
printf("\033[2J"); //输出标题,因为这个软件名字叫: SinDemo
printf("\033[1;1H | SinDemo |\t"); /**
* 这里主要是完成那一行重复的0-9,SIN_AMPLITUDE*2是因为sin曲线的
* 最高点和最低点是2倍的幅值
*/
printf("\033[%d;1H\t", Y_NUMBER_BEGIN_LINE);
for (i = ; i < SIN_AMPLITUDE*; i++)
printf("%d", i%);
printf("\n"); /**
* while循环主要完成内容:
* 1. 每次循环对局部变量重新初始化;
* 2. 将下一屏图形的初始角度赋值给当前的图形初始角;
* 3. 将下一屏图形的初始角度加上间隔角度(TRIANGLE/SIN_AMPLITUDE),
* TRIANGLE/SIN_AMPLITUDE在本Demo中是360/20=18度,就相当于X轴
* 每格代表18度
* 2. 调整光标到固定的位置;
* 3. 重新绘制整屏图形;
*/
while(){ //重新初始化局部变量,因为每一屏图形都像一个新的开始
i = ;
offsetCenter = ;
lineNumber = ;
currentInitradian = ; //从nextInitAngle中获取当前的初始化角度
currentInitAngle = nextInitAngle; //为下一次循环提供下一次的初始化角度
nextInitAngle += TRIANGLE/SIN_AMPLITUDE; //将光标移动到开始绘图的位置去
printf("\033[%d;1H", SIN_GRAPH_BEGIN_LINE); /**
* 根据不同的情况绘制图形, 每一次循环,就是绘制了图形中的一行
*/
while(){
//判断是不是最后一行,lineNumber起始行是从0开始
if(lineNumber == SIN_AMPLITUDE){
//打印最后一行前面的数字行号
printf("\033[%d;1H%04d\t", lineNumber+SIN_GRAPH_BEGIN_LINE, lineNumber);
for (i = ; i < SIN_AMPLITUDE*; i++)
/**
* 判断是否到达中间位置,因为中间位置要放V的箭头,同时在旁边输出一个X,
* 代表这是X轴方向.
*/
i == SIN_AMPLITUDE ? printf("V X") : printf(" ");
break;
} /**
* 对currentInitAngle角度进行修整,比如370度和10度是对应相同的sin值
* 其实这一步可以不用,但是这里保留了,后面是将currentInitAngle角度制的值
* 换算成对应的弧度制的值,便于sin求值.
*/
currentInitAngle = ((int)currentInitAngle)%((int)TRIANGLE);
currentInitradian = currentInitAngle/(TRIANGLE/)*PI; /**
* 算出当前次currentInitradian对应的sin值,并乘以幅值SIN_AMPLITUDE,获取sin曲线
* 在Y轴上相对于中心轴的偏移offsetCenter,offsetCenter可能是正值,也可能是负值,
* 因为中心轴在中间.
*/
offsetCenter = (int)(sin(currentInitradian)*SIN_AMPLITUDE); /**
* 在正确的地方输出正确的行号 :)
*/
printf("\033[%d;1H%04d", lineNumber+SIN_GRAPH_BEGIN_LINE, lineNumber); //用一个制表符,给出行号与图形的空间距离
printf("\t"); /**
* 第一行,和其他的行不一样,有区别,输出结果如下:
* 0000 ------------@-------+--------------------> Y
*/
if(lineNumber == ){
for (i = ; i < SIN_AMPLITUDE*; i++){
/**
* 判断当前输出的字符位置是否是X,Y轴交叉的位置,如果是就输出'+',
* 不是就输出'-'
*/
i == SIN_AMPLITUDE ? printf("+") : printf("-");
/**
* 判断当前输出的字符位置是否是sin曲线上的点对应的位置,
* 如果是就输出'@'
*/
if(i == offsetCenter+SIN_AMPLITUDE)
printf("@");
}
//代表这个方向是Y轴
printf("-> Y\n");
} else {
for (i = ; i < SIN_AMPLITUDE*; i++){
//判断当前输出的字符位置是否是sin曲线上的点对应的位置,如果是就输出'*'
if(i == (offsetCenter+SIN_AMPLITUDE)){
printf("*");
//判断当前输出的字符位置是否是X轴上对应的位置,如果是就输出'|'
}else if(i == SIN_AMPLITUDE){
printf("|");
}else{
/**
* 这里主要是要处理一行里面除了画'*'、'|'、之外的'-'、' '
* 其中的SIN_AMPLITUDE到SIN_AMPLITUDE+offsetCenter正好就是需要输出'-'的地方
* 其他的地方输出' '
*/
(((i > SIN_AMPLITUDE) && (i < SIN_AMPLITUDE+offsetCenter)) || \
((i < SIN_AMPLITUDE) && (i > SIN_AMPLITUDE+offsetCenter))) \
? printf("-") : printf(" ");
}
//行尾,输出换行符
if(i == (SIN_AMPLITUDE*-))
printf("\n");
}
} /**
* 一行输出完成,为下一行输出作准备,下一行比上一行在角度上多加TRIANGLE/SIN_AMPLITUDE,
* 在本Demo中相当于360/20=18,也就是加18度.
*/
currentInitAngle += TRIANGLE/SIN_AMPLITUDE; //行号加1
lineNumber++;
}
/**
* 一屏图像输出完毕,最后输出一个换行符,并且延时一段时间再开始绘制下一屏图形
*/
printf("\n");
usleep(DELAY_TIME);
} return ;
}

C语言与VT100控制码编程的更多相关文章

  1. VT控制码

    VT100 是一个终端类型定义,VT100 控制码是用来在终端扩展显示的代码.比如果终端上任意坐标用 不同的颜色显示字符. 所有的控制符是 \033 打头(即 ESC 的 ASCII 码)用输出字符语 ...

  2. C语言控制台窗体图形界面编程(总结)

    本系列文章是笔者通过学习<C语言控制台窗体界面编程(修正版)>而写(关于此文档的很多其它信息请看本系列文章第一篇),旨在让大家更加清晰简洁easy地学习C语言控制台窗体界面的编程. 通过本 ...

  3. 字节码编程,Byte-buddy篇一《基于Byte Buddy语法创建的第一个HelloWorld》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相对于小傅哥之前编写的字节码编程: ASM.Javassist 系列,Byte Bu ...

  4. 字节码编程,Javassist篇二《定义属性以及创建方法时多种入参和出参类型的使用》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一篇 Helloworld 中,我们初步尝试使用了 Javassist字节编程的 ...

  5. cpu的控制单元与语言中的控制逻辑有没有关系?

    cpu的控制单元与语言中的控制逻辑有没有关系?

  6. R语言基于S4的面向对象编程

    前言 本文接上一篇文章 R语言基于S3的面向对象编程,本文继续介绍R语言基于S4的面向对象编程. S4对象系统具有明显的结构化特征,更适合面向对象的程序设计.Bioconductor社区,以S4对象系 ...

  7. Go语言面组合式向对象编程基础总结

    转自:http://blog.csdn.net/yue7603835/article/details/44282823 Go语言的面向对象编程简单而干净,通过非侵入式接口模型,否定了C/C++ Jav ...

  8. Python语言基础07-面向对象编程基础

    本文收录在Python从入门到精通系列文章系列 1. 了解面对对象编程 活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编 ...

  9. C语言与汇编的嵌入式编程:求100以内素数

    写汇编之前,需要搞清楚C语言代码的写法,这里以最简单的算法举例说明 C代码如下: #include <stdio.h> void main(){ int i,j; ; ;i<=;i+ ...

随机推荐

  1. MongoDB(课时9 范围运算)

    3.2.2.4 范围查询 只要是数据库,必须存在有“$in”(在范围之中).“$nin”(不在范围之中). 范例:查询姓名是“张三”,“李四”,“王五” db.students.find({" ...

  2. TinyXml节点查找及修改

    参考:http://blog.csdn.net/zww0815/article/details/8083550 // 读者对象:对TinyXml有一定了解的人.本文是对TinyXml工具的一些知识点的 ...

  3. R-CNN(Rich feature hierarchies for accurate object detection and semantic segmentation)论文理解

    论文地址:https://arxiv.org/pdf/1311.2524.pdf 翻译请移步: https://www.cnblogs.com/xiaotongtt/p/6691103.html ht ...

  4. 【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试

    前言 Mock是一个做自动化测试永远绕不过去的话题.本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法. 可能有的小伙伴不太了解mock在实际自动化测试过程中 ...

  5. [.NET开发] C# 如何创建Excel多级分组

    要设置显示或者隐藏分类数据下的详细信息,在便于数据查看.管理的同时也使文档更具美观性.那么,在C#中如何来创建Excel数据的多级分组显示呢?下面将进行详细阐述.方法中使用了免费版组件Free Spi ...

  6. 3-23 Rspec自动化测试(开始练习)

    闰年程序 leap_year_spec.rb require_relative './leap_year' describe "Leap Year" do it "201 ...

  7. 3-18/19 (自我练习)30多个《Ruby元编程》的spell(pattern)小例子。

    Spell,也称pattern,idiom # Around Alias:从一个重新定义的方法中调用原始的,被重命名的版本. # old_reverse是未改变的原始方法,reverse/new_re ...

  8. android--------自定义视频控件(视频全屏竖屏自动切换)

    android播放视频也是常用的技术,今天分享一个自定义视频控件,支持自定义控制 UI,全屏播放, 可以实现自动横竖屏切换的控件,跟随手机的位置而,重力感应自动切换横竖屏. 效果图:   代码下载Gi ...

  9. P4721 【模板】分治 FFT

    其实是分治ntt,因为fft会爆精度,真*裸题 分治过程和fft的一模一样,主要就是ntt精度高,用原根来代替fft中的\(w_n^k\) 1.定义:设m>1,(a,m)==1,满足\(a^r= ...

  10. DATEADD 和 CAST 函数

    where cast(soh.orderdate as date)=cast(dateadd(day,-1,getdate()) as date) 这一代码在sql 2005 不适用,使用conver ...