✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、回车和换行
2、观察回车换行现象
3、缓冲区
4、usleep和fflush函数
5、简单倒计时
6、进度条
6.1、版本一
6.2、版本二
总结
1、回车和换行
回车:
指光标由行中任意位置移动到行首。
换行:
指换到下一行的情况。
\r:只进行回车
\n:进行回车和换行
2、观察回车换行现象
1.执行下面代码
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello linux\n");
sleep(3);
return 0;
}
现象:
打印完字符串,然后休眠3秒,最后结束程序。
2.执行下面代码
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello linux\r");
sleep(3);
return 0;
}
现象:
休眠3秒,程序就结束了。
为什么使用\r就不能字符串了呢???
这会牵扯到缓冲区的知识,我们在写一个缓冲区的点来详细讲解!!!
补充查询手册:
输入命令:
mam 3 sleep # 三号手册才能查到我们想用的sleep
3、缓冲区
概念:
在内存中预留了一块空间,用来缓冲输入或输出的数据,这个保留的空间被称为缓冲区。
那为什么字符串结尾有\n就会打印字符串,而有\r则不打印字符串呢???
由于显示器模式是行刷新缓冲区是按行缓冲的。而字符串是先拷贝在内存空间中,当遇到\n的时候,就会强制把字符串刷新到显示器文件上,因此就看到该字符串了。
而字符串后面是\r的情况,不会强制把字符串刷新到显示器,但是当遇到return 0;时,会强制刷新到显示器上,但是由于\r 会回到行首后,会进行覆盖写,shell 提示符会覆盖掉之前写的 “hello world”。
注意:缓冲区满了也会刷新缓冲区。
如果我们想自己手动刷新缓冲区该怎么办呢???
可以通过调用刷新缓冲区的函数,我们在下面通过代码进行讲解。
4、usleep和fflush函数
我们可以通过man手册先查询两个函数的基本用法。
1. usleep函数
功能:
以微秒为间隔暂停执行,头文件为#include<unistd.h>
2.fflush函数
功能:
重刷一个流。
stdout -- 标准输出流 -- 屏幕
stdin -- 标准输入流 -- 键盘
stderr -- 标准错误流 -- 屏幕
3.代码演示
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello linux\r");
fflush(stdout); # 强制刷新输出流
sleep(3); # 休眠3秒
return 0;
}
现象:
先打印字符串,然后休眠3秒,字符串被shell提示符覆盖。
5、简单倒计时
通过上面的简单重刷输出流,我们可以做一个简单的倒计时程序。
要求:实现一个从10到0的倒计时效果。
代码:
#include<stdio.h>
#include<unistd.h>
int main()
{
int cnt = 10;
while(cnt >= 0)
{
printf("%-2d\r",cnt);// 以2位的固定位宽输出且左对齐方式打印
fflush(stdout);//重刷缓冲区
sleep(1);//休眠1秒,只有休眠才能看到倒计时的效果
cnt--;
}
printf("\n");
return 0;
}
分析:
1. 定义倒计时变量 cnt,让其逐渐递降。
2. 核心就是用 \r 回到缓冲区行首进行覆盖写,然后fflush不断刷新出出来,但是只是刷新并不能看到效果,因为它会直接被覆盖,因此需要休眠一会,几秒自己设置。
3. 格式调整,打印 cnt==10 时,在缓冲区打印的其实是字符1和字符0,如果我们不用 %2d(以2位的固定位宽输出) 来调整格式,而用 d% 的话,那么覆盖写只会覆盖第一位字符 1 的位置,而第二位的字符 0 还留在缓冲区原来的位置,于是倒计时便会变为下面这样
10->90->80->70->60->50->40->30->20->10->00 ,-2d% 加个负号保证其向左对齐。
4. 倒计时完加个 \n符,shell 提示符就不会覆盖倒计时。
6、进度条
6.1、版本一
此处我们安装C语言中的分文件实现此进度条,加上我们Linux中学习的makefile工具。
首先我们创建四个文件:processbar.c processbar.h main.c makefile
processbar.c : 进度条函数的实现。
processbar.h : 进度条函数的声明,头文件包含。
main.c : 调用.h文件中的方法 。
makefile : 自动化编译。
进度条效果:
- 第一个中括号就是表示进度条。
- 第二个中括号表示进度。
- 第三个表示旋转样式。
原理基本跟倒计时一样。
[jkl@VMCentos7 process]$ touch processbar.h processbar.c main.c makefile # 创建四个文件
[jkl@VMCentos7 process]$ ls
main.c makefile processbar.c processbar.h
makefile文件代码:
processbar:processbar.c main.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f processbar
processbar.h代码:
#includ<stdio.h>
#include<unistd.h>
#include<string.h>
void ProcBar();
processbar.c代码:
#include "processbar.h"
//字符串长度
#define Length 101
#define Style '#' //表示进度条的符号
const char* label="|/-\\";//两个\\表示一个\,表示旋转样式
//version1
void ProcBar()
{
char bar[Length];//缓冲区长度
memset(bar,'\0',sizeof(bar));//将缓存区空间都改为\0
int len=strlen(label);//字符串长度
//循环往缓存区输入#
int cnt=0;
while(cnt<=100)
{
printf("[%-100s][%3d%%][%c]\r",bar,cnt,label[cnt%len]);
fflush(stdout);
bar[cnt++]=Style;
usleep(30000);//休眠30000微秒,秒太长了
}
printf("\n");
}
main.c代码:
#include "processbar.h"
int main()
{
ProcBar();
return 0;
}
运行中的效果:
最后的效果:
6.2、版本二
在我们的实际生活中,进度条一般不会单独出现,常出现在下载界面和游戏界面,因此版本二通过下载场景来进行展示。
makefile文件代码:
download:download.c test.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f download
download.h代码:
#pragma once
#include <stdio.h>
#include <string.h>
#include <unistd.h>
typedef void(*callback_t)(double, double);//函数指针
//void ProcBar();
void ProcBar(double total, double current);
download.c代码:
#include"download.h"
#define Length 101
#define Style '='
const char *lable = "|/-\\";
//version 2
void ProcBar(double total, double current)
{
char bar[Length];
memset(bar, '\0', sizeof(bar));
int len = strlen(lable);
int cnt = 0;
double rate = (current*100.0)/total;
int loop_count = (int)rate;
while(cnt <= loop_count)
{
bar[cnt++] = Style;
//usleep(20000);
}
printf("[%-100s][%.1lf%%][%c]\r", bar, rate, lable[cnt%len]);
fflush(stdout);
}
test.c代码:
#include"download.h"
double bandwidth = 1024*1024*1.0;
//download
void download(double filesize,callback_t cb)
{
double current = 0.0;
printf("download begin, current: %lf\n", current);
while(current <= filesize)
{
cb(filesize, current);
//从网络中获取数据
usleep(100000);
current += bandwidth;
}
printf("\ndownload done, filesize: %lf\n",filesize);
}
int main()
{
download(100*1024*1024,ProcBar);
download(2*1024*1024,ProcBar);
return 0;
}
总结
本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!