【Linux】理解C/C++中的换行符、缓冲区刷新与进度条实现

【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;
    }
    
    
  • 运行结果:
    请添加图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值