【Linux】理解C/C++中的换行符、缓冲区刷新与进度条实现
本文通过进度条实现,讲解深入理解C/C++中的换行符、缓冲区刷新策略。
\r 和 \n 的区别
\r和\n的区别源于早期打字机的机械设计:
- **\n **表示"换行" - 将纸张向上移动一行
- \r 表示"回车" - 将打字头移回行首
在现在,Linux和Windows环境下结果不一样,对于\r也有新的玩法。
现代系统中的表现
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("=== \\r 和 \\n 的区别演示 ===\n\n");
// 演示 \n - 正常换行
printf("使用 \\n 换行:\n");
printf("第一行\n");
printf("第二行\n");
printf("第三行\n\n");
// 演示 \r - 覆盖当前行
printf("使用 \\r 覆盖:\n");
printf("这行会被覆盖\r");
fflush(stdout);
usleep(500000); // 等待0.5秒
printf("新内容覆盖了旧内容\n\n");
return 0;
}
不同操作系统的行结束符
| 操作系统 | 行结束符 |
|---|---|
| Unix/Linux | \n |
| Windows | \r\n |
-
对于这点,在文件书写时候要注意。
-
代码:
// Unix/Linux 风格 fprintf(fp, "Unix line\n"); // Windows 风格 fprintf(fp, "Windows line\r\n");
C/C++ 缓冲区刷新策略
缓冲区缓冲策略
C/C++中有三种主要的缓冲策略:
-
全缓冲:缓冲区满时才刷新,通常用于文件操作
-
行缓冲:遇到换行符时刷新,通常用于终端输出
-
无缓冲:立即输出,通常用于错误输出(比如cpp的cerr,c的stderr)
缓冲区刷新方法
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("=== 缓冲区刷新策略演示 ===\n\n");
// 1. 使用 fflush() 强制刷新
printf("这行文字没有换行符");
fflush(stdout); // 强制刷新输出缓冲区
usleep(1000000); // 等待1秒
printf(" - 现在添加换行\n");
// 2. 使用 \n 触发行缓冲刷新
printf("这行有换行符,会立即显示\n");
// 3. 使用 stderr (无缓冲)
fprintf(stderr, "错误信息立即显示 (stderr无缓冲)");
// 4. 程序结束时自动刷新
printf("程序结束时会自动刷新所有缓冲区");
// 注意:这里没有换行符,但程序结束时会刷新
return 0;
}
实际应用场景
// 日志系统 - 需要及时刷新
void log_message(const char* message)
{
printf("[%s] %s\n", get_timestamp(), message);
fflush(stdout); // 确保日志立即写入
}
// 进度显示 - 需要实时更新
void show_progress(int percent)
{
printf("\r进度: %d%%", percent);
fflush(stdout); // 立即显示,不等待换行
}
简单的倒计时实现
巩固上面的知识
#include <stdio.h>
#include <unistd.h>
int main()
{
int i = 10;
while(i >= 0)
{
printf("%-2d\r", i); // \n
fflush(stdout);
i--;
sleep(1);
}
printf("\n");
return 0;
}
进度条实现
可以根据文章makefile的使用_code 的makefile怎么用-优快云博客,写一个Makefile文件完成编译,也可以直接gcc,对于vim的相关操作查看文章初识Linux-优快云博客
简单的进度条
- 代码:
#pragma once
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define FILL '#'
#define LENGTH 101
void Process()
{
// 旋转光标
const char* symbol = "|\\-/";
int s_len = strlen(symbol);
// 模拟加载
int speed = 1;
double total = 0;
double goal = 1024.0;
// 进度条主体
char bar[LENGTH];
memset(bar, '\0', sizeof(bar));
static int index = 0;
while(total <= goal)
{
index++;
index %= s_len;
// 进度比率
usleep(5000); // 模拟加载
total += speed;
double rate = total / goal * 100;
// 填充符号
int sym_size = (int)rate;
for(int i = 0; i < sym_size; i++)
{
bar[i] = FILL;
}
// 打印,记得带上\r
printf("[%-100s][%lf%%][%c]\r", bar, rate, symbol[index]);
fflush(stdout); // 刷新缓冲区
if(rate >= (double)100)
{
printf("[%-100s][%lf%%][%c]\n", bar, rate, symbol[index]);
break;
}
}
}
- 运行结果

优雅的进度条
上文的进度条只能说是,展现出进度条的样子,但是并不算是合格的进度条,我们可以写的更优雅一点
-
代码:
// 这里我们实现了一个==================>效果的进度条 void basic_progress_bar(int total, int current) { int bar_width = 50; int pos = (current * bar_width) / total; printf("\r进度: ["); for (int i = 0; i < bar_width; i++) { if (i < pos) printf("="); else if (i == pos) printf(">"); else printf(" "); } printf("] %d%%", (current * 100) / total); fflush(stdout); } int main() { for (int i = 0; i <= 100; i++) { basic_progress_bar(100, i); usleep(50000); // 模拟工作 } printf("\n完成!\n"); return 0; }当然,你也可以加一些其他的小功能:比如根据速度预估剩余下载时间、打印上下载速度的单位,让看起来更舒服等等。
-
这是一个较为完整的模拟文件下载的代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // 模拟文件下载进度回调 typedef struct { FILE* file; long total_size; long downloaded; char* filename; } DownloadContext; int progress_callback(void* clientp, double dltotal, double dlnow, double ultotal __attribute__((unused)), double ulnow __attribute__((unused))) { DownloadContext* ctx = (DownloadContext*)clientp; if (dltotal > 0) { int percent = (int)((dlnow / dltotal) * 100); printf("\r下载进度: %d%% (%.2f/%.2f MB) - %s", percent, dlnow/1024/1024, dltotal/1024/1024, ctx->filename); fflush(stdout); } return 0; } // 模拟文件写入 size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) { FILE* file = (FILE*)userp; size_t written = fwrite(contents, size, nmemb, file); return written; } void simulate_download(const char* filename, long total_size) { FILE* file = fopen(filename, "wb"); if (!file) { printf("无法创建文件: %s\n", filename); return; } DownloadContext ctx = {file, total_size, 0, (char*)filename}; // 模拟下载过程 for (long i = 0; i <= total_size; i += 1024) { // 模拟写入数据 char buffer[1024]; memset(buffer, 'A' + (i % 26), sizeof(buffer)); fwrite(buffer, 1, sizeof(buffer), file); // 更新进度 progress_callback(&ctx, total_size, i, 0, 0); usleep(10000); // 模拟网络延迟 } fclose(file); printf("\n下载完成: %s\n", filename); } int main() { printf("=== 文件下载进度条演示 ===\n"); // 模拟下载几个不同大小的文件 simulate_download("small_file.txt", 1024 * 100); // 100KB usleep(500000); simulate_download("medium_file.txt", 1024 * 1024); // 1MB printf("\n所有文件下载完成!\n"); return 0; } -
运行结果:

1071

被折叠的 条评论
为什么被折叠?



