Linux第一个系统程序——进度条
1.回车与换行
回车(CR,
):
- 作用:将光标移动到当前行的行首(最左侧),但不换到下一行。
- 历史来源:源自打字机的“回车”操作——打字机的滑架(Carriage)需要手动推回行首。
- ASCII码:
0x0D
(十六进制),或
换行(LF,
):
- 作用:将光标移动到下一行的同一列位置(垂直向下移动一行),但不改变水平位置。
- 历史来源:打字机的“换行”操作——滚筒旋转一行。
- ASCII码:
0x0A
(十六进制),或
printf(“hello world!
”);
这里的
把回车换行的动作都做了,因为C语言会根据当前操作系统自动处理
,无需手动写
(Windows)或
(Unix)。
2.行缓冲区
下面的这段代码执行会有什么现象?
#include
#include
int main()
{
printf("hello world!
");
sleep(2);
return 0;
}
答案是,这里显示器打印出hello world!,然后再sleep两秒钟程序结束。
那么要是把 去掉,代码执行会有什么现象?
#include
#include
int main()
{
printf("hello world!");
sleep(2);
return 0;
}
我们执行后发现,刚开始程序不显示,过了两秒之后,程序快结束时,hello world!打印出来。
那为什么有 显示器打印是立即显示呢?而没有 程序是过了两秒再打印呢?sleep在printf之前执行??
我们要知道,C语言执行代码时,一直是从上向下执行的,所有一定是先执行printf,再执行sleep。但为什么打印的内容也是在休眠2秒之后才显示呢?这期间已执行完的内容在哪?——缓存区!!
缓冲区是给显示器提供的,所以只要有缓冲区就必然存在一个刷新策略!
显示器的刷新策略:行刷新!所以有 ,立即显示;没有 ,整个程序结束,然后显示。
如果我们就是想让这个不带 的程序立即刷新怎么办?
C提供了对应的fflush策略,可以直接强制刷新。
stdin对应键盘
stdout,stderror对于显示器
printf打印,是把信息写在stdout(标准输出)里面,所以打印时信息没有刷出来,就用fflush刷新一下标准输出,就可以把这个字符串立即刷新出来。
#include
#include
int main()
{
printf("hello world!");
fflush(stdout);
sleep(2);
return 0;
}
3.demo——倒计时程序
字符输入到显示器上什么位置由谁决定?
是由光标决定。
如果我们的光标在第一个位置,写入9,这时光标会向后移动,但我们想办法让光标再移动到开头,再向里面写8,…,按照这样的原理,我们就可以很容易的实现一个倒计时程序。
#include
#include
int main()
{
int cnt=9;
while(cnt>=0)
{
printf("%d
",cnt);
cnt--;
sleep(1);
}
return 0;
}
这虽然是个倒计时,但并不是我们想要的那种。
今天我们了解了回车,那就可以修改一下我们的代码。
#include
#include
int main()
{
int cnt=9;
while(cnt>=0)
{
printf("%d
",cnt);
cnt--;
sleep(1);
}
return 0;
}
我们执行后发现,它竟然什么都不打印了!?
哦哦!!原来是因为把 变成 后,没有 了,所以数字会被写在缓存区里,一直不被更新。
那我们刷新一下吧。
#include
#include
int main()
{
int cnt=9;
while(cnt>=0)
{
printf("%d
",cnt);
fflush(stdout);
cnt--;
sleep(1);
}
return 0;
}
执行后发现一切正常。?
那0为什么被覆盖掉了?
原来当我们程序退出时,命令行也要打命令行字符串,所以就会把0覆盖掉。
所以我们最后再加一个打印换行符,不然0就会被覆盖掉。
#include
#include
int main()
{
int cnt=9;
while(cnt>=0)
{
printf("%d
",cnt);
fflush(stdout);
cnt--;
sleep(1);
}
printf("
");
return 0;
}
此时就很完美了。
如果我们要是从10开始倒计时呢?修改后发现为什么只有个位数变化呢?!?
其实显示器只认字符,显示器是字符设备。
我们在显示器上打的10,其实不是十,而是1和0两个字符。
我们再修改一下,指定输出十进制整数,宽度至少2个字符,不足补空格。不过%2d默认是左对齐补位(空格填充在数值的左侧),我们想要右对齐,可使用%-2d。
#include
#include
int main()
{
int cnt=10;
while(cnt>=0)
{
printf("%-2d
",cnt);
fflush(stdout);
cnt--;
sleep(1);
}
printf("
");
return 0;
}
这样就可以了。
4.进度条
我们想写一个进度条不断的推进,旁边有一个百分比不断在增长,和一个不断在旋转的光标。
process.c
#include "process.h"
#include
#include
#define SIZE 101
#define STYLE '#'
//v2:根据进度,动态刷新一次进度条
void FlushProcess(const char *tips,double total,double current)
{
const char *lable = "|/-";
int len = strlen(lable);
static int index =0;
char buffer[SIZE];
memset(buffer,0,sizeof(buffer));
double rate = current*100.0/total;
int num=(int)rate;
int i = 0;
for(;i < num;i++)
buffer[i]=STYLE;
printf("%s...[%-100s][%.1lf%%][%c]
",tips,buffer,rate,lable[index++]);
fflush(stdout);
index %= len;
if(num >= 100)printf("
");
}
//v1:展示进度条基本功能
void process()
{
int rate=0;
char buffer[SIZE];
memset(buffer,0,sizeof(buffer));
const char *lable="|/-";
int len = strlen(lable);
while(rate <= 100)
{
printf("[%-100s][%d%%][%c]
",buffer,rate,lable[rate%len]);
fflush(stdout);
buffer[rate] = STYLE;
rate++;
usleep(10000);
}
printf("
");
}
process.h
#pragma once
#include
//v1
void process();
//v2
void FlushProcess(const char* ,double total,double current);
main.c
#include "process.h"
#include
#include
#include
//函数指针类型
typedef void (*call_t)(const char*,double,double);
double total = 1024.0;
//double speed[]=1.0;
double speed[]={1.0,0.5,0.3,0.2,0.1,0.01};
void download(int total,call_t cb)
{
srand(time(NULL));
double current=0.0;
while(current <= total)
{
cb("下载中",total,current);//进行回调
if(current >= total) break;
//下载代码
int random = rand() % 6;
usleep(5000);
current += speed[random];
if(current>=total) current=total;
}
}
void upload(int total,call_t cb)
{
srand(time(NULL));
double current=0.0;
while(current <= total)
{
cb("上传中",total,current);//进行回调
if(current >= total) break;
//下载代码
int random = rand() % 6;
usleep(5000);
current += speed[random];
if(current>=total) current=total;
}
}
int main()
{
download(1024.0,FlushProcess);
printf("download 1024.0 MB done
");
download(512.0,FlushProcess);
printf("download 512.0 MB done
");
download(256.0,FlushProcess);
printf("download 256.0 MB done
");
download(128.0,FlushProcess);
printf("download 128.0 MB done
");
download(64.0,FlushProcess);
printf("download 64.0 MB done
");
upload(500.0,FlushProcess);
return 0;
}
Makefile
BIN=process
#SRC=$(shell ls *.c)
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
CC=gcc
RM=rm -f
$(BIN):$(OBJ)
@$(CC) $^ -o $@
@echo "链接 $^ 成 $@"
%.o:%.c
@$(CC) -c $< #-g
@echo "编译 ... $< 成 $@"
.PHONY:clean
clean:
@$(RM) $(OBJ) $(BIN)
.PHONY:test
test:
@echo $(BIN)
@echo $(SRC)
@echo $(OBJ)