目录
前言:
1、回车与换行
历史背景
不同操作系统中的使用
标准输入输出函数
2、行缓冲区
3、进度条version1
4、进度条version2(模拟下载环境)
前言:
在实现进度条之前,这里我们要先铺垫两个概念——回车换行与行缓冲区。
1、回车与换行
在计算机科学和文本处理中,“回车”(Carriage Return,简称 CR,ASCII 码为 13)和“换行”(Line Feed,简称 LF,ASCII 码为 10)是两个不同的概念,但在实际使用中,它们经常联系在一起,用于表示文本行的结束和新行的开始。
历史背景
- 回车(Carriage Return, CR, \r):在早期的打字机或电传打字机中,回车操作会使打印头移动到当前行的最左端(即回到行首)。
- 换行(Line Feed, LF, \n):换行操作会使打印头移动到下一行的同一位置(即向下移动一行)。
不同操作系统中的使用
- Unix/Linux/macOS:这些系统使用 LF(\n)作为行结束符。
- Windows:Windows 系统最初使用 CR+LF(\r\n)作为行结束符,这是从早期的 DOS 系统继承下来的。这种组合最初是为了兼容当时的打印机和终端。
- 经典 Mac OS(9 及更早版本):这些系统使用 CR(\r)作为行结束符。
在C语言中,处理文本行结束符时,同样需要考虑到不同操作系统中回车(Carriage Return, CR, \r
)和换行(Line Feed, LF, \n
)的差异。然而,C语言标准库提供了一些函数和宏,可以帮助程序员处理这些差异,而无需过多关注底层细节。
标准输入输出函数
C语言标准库中的printf
、scanf
、fgets
、fputs
等函数通常会自动处理行结束符,以适应不同的操作系统。例如,当使用printf
输出一个字符串并包含一个换行符\n
时,在Windows系统上,标准输出库函数会将其转换为\r\n
(如果输出到控制台或文件),而在Unix/Linux/macOS系统上,则只会输出\n
。
2、行缓冲区
我们先来看三个个现象:
现象一:
先打印并换行。
现象二:
现象三:
先打印不回车换行。
第一个问题:为什么我们在基于现象一后将源文件中的\n去掉后会出现现象二呢?
难道是程序先执行sleep函数,再执行printf函数吗?答案是否定的,在我们学习C语言时就知道,程序从main函数开始从上至下执行代码。所以,这里一定是先执行printf函数,再执行sleep函数。
那原因到底是什么呢?
这里我们就要引入行缓冲区的概念了:
在C语言中,行缓冲区(line-buffered)是三种类型的输入/输出缓冲区之一,另外还有全缓冲区(fully-buffered)和无缓冲区(unbuffered)。行缓冲区的行为取决于所使用的标准I/O函数和流(stream)。
行缓冲区的特点:当遇到换行符(\n
)或者缓冲区满时,缓冲区内容会被刷新(即写入到目标设备)。
解释:当printf函数执行完后,其内容并没有打印到显示器上,而是在缓冲区里面(没有遇到换行符(\n
)或者缓冲区满)。然后执行完sleep函数后,程序结束会自动刷新缓冲区,最后才打印出内容“Hello Linux!”。
第二个问题:现象三为什么会先打印后休眠?
这里我们就介绍一下fflush函数:
fflush(FILE *stream)
:刷新指定流的缓冲区。即使缓冲区没有满或没有遇到换行符,调用这个函数也会强制将缓冲区内容写入目标设备。
3、进度条version1
#include"process.h"
#define NUM 101
#define STYLE '='
void processbar()
{
//buffer数组模拟进度条
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
//lable字符串模拟旋转效果
const char* lable = "\\-|/";
int size = strlen(lable);
int cnt = 0;
while (cnt <= 100)
{
printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt % size]);
fflush(stdout);
buffer[cnt] = STYLE;
usleep(50000);
cnt++;
}
printf("\n");
}
效果演示:
4、进度条version2(模拟下载环境)
第一个版本我们只是将进度条的运行效果演示成功了,在实际程序下载的过程中我们不可能直接将上面的程序当做进度条,不然很大可能,我们的程序还没有下载完,而进度条就已经跑完了,这是不合理的!
所以,在实际场景中,我们要根据下载的总量和当前的下载量,来决定进度条的进度!
具体如何操作,大家看下面的代码就完全可以看懂啦!(非常简单的)
process.c
#include"process.h"
void process(double total, double cur)
{
//改函数会被下载的任务频繁调用,所以cnt设为静态
static int cnt = 0;
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
//模拟旋转效果
const char* lable = "\\-|/";
int len = strlen(lable);
//找到当前进度的位置
int num = (int)(cur * 100) / total;
int i = 0;
for (; i < num; i++)
{
buffer[i] = STYLE;
}
//不再循环打印,直接根据当前下载进度打印出进度条
printf("[%-100s][%.1f%%][%c]\r",
buffer, cur / total * 100, lable[cnt % len]);
fflush(stdout);
cnt++;
}
main.c
#include"process.h"
double total = 1024.0;
double speed = 1.0;//加载速度
//该函数用于模拟程序的下载
void DownLoad()
{
double cur = 0;
while (cur <= total)
{
process(total, cur);
usleep(3000);
cur += speed;
}
printf("\n");
}
int main()
{
DownLoad();
DownLoad();
DownLoad();
DownLoad();
return 0;
}
效果演示: