C 语言中的格式化输出和转义字符

格式化输出字符

常用的格式化字符

C 语言中的格式化输出字符如下

格式字符 类型 备注
%[-][0][n]d int - 表示左对齐,n 表示输出宽度,0 表示右对齐时用 0 补齐,左对齐时用空格补齐
%c char
%[-][0][n][.n]f float .n 表示小数位位数,其余同int
%[-][0][n][.n]s char[] .n 表示从左开始取 n 位,其余同int
%hd short
%[#]o 八进制整型 # 表示显示八进制前面的0
%[#]x 十六进制整型 # 表示显示十六进制前面的0x
%hu unsigned short
%ld/%Ld long
%lld/%LLd long long
%u unsigned int
%ul/lu/UL/LU unsigned long int
%ull/llu/ULL/LLU unsigned long long int
%lf double 参数同上
%e 科学计数法 1e2=1*10^22e-2=2*10^(-2)0.5E+3=0.5*10^(3)
%g 自动判断使用f还是e
%p point
%% 输出%

* 修饰符

以格式化输出整数为例,如果想要动态地控制输出的整数的宽度,需要用到 * 修饰符

int width = 6, precision = 2, n = 1;
float f = 3.1415926;
printf("######\n");
printf("%0*d\n", width, n);
printf("%*.*f", width, precision, f);

输出如下

######
000001
  3.14

转义字符

常用的转义字符

一般的转义字符

转义序列 作用
\n 换行
\t 水平制表符
\\ 反斜杠
\' 单引号
\" 双引号
\r 光标回到当前行行首
\b 退格

一些案例

1. 用户交互

使用 \b 实现在横线上输入

int num;
printf("输入一个两位数:__\b\b");
scanf("%d", &num);

2.打印进度条

利用 \r 回到行首不断更新进度条

#include <stdio.h>
#include <unistd.h> // 用于 usleep (微秒级暂停)

int main()
{
    int total = 50; // 进度条总长度
    printf("开始下载...\n");

    for (int i = 0; i <= total; i++)
    {
        // \r 让光标回到行首
        // \033[32m 设置绿色
        printf("\r[");

        // 打印已完成的方块
        for (int j = 0; j < i; j++)
            printf("=");
        // 打印未完成的空格
        for (int j = 0; j < total - i; j++)
            printf(" ");

        // \033[0m 重置颜色,显示百分比
        printf("] %d%%", i * 2);

        // 强制刷新缓冲区,否则看不到动画
        fflush(stdout);

        usleep(50000); // 暂停 50 毫秒
    }
    printf("\e[0m");

    printf("\n下载完成!\n");
    return 0;
}

编码相关

转义序列 作用 例子
\xhh 给定一个十六进制数,输出 ASCII 码表中其对应的字符 \x41 对应字符 A
\ooo 给定一个八进制数,输出 ASCII 码表中其对应的字符 \101 对应字符 A
\uNNNN 输出对应 Unicode 表中对应的字符 参考 Wikipedia \u4f60 对应汉字 你

ANSI 转义序列

ANSI 转义序列(ANSI Escape Sequences)是一系列以 ESC(ASCII 码 27,即 \033\x1B)​​ 开头的控制字符序列

所有的 ANSI 转义序列都始于一个特殊的字符:ESC (Escape,转义字符)。在编程语言中,它通常被表示为 \e (PHP, Bash) 或八进制的 \033 (Python, C/C++),其 ASCII 码为 27

我们常用的 ANSI 转义序列都是 CSI 序列,CSI 序列由ESC[、若干个(包括 0 个)参数字节、中间字节、最终字节组成,各部分字节的范围如下

组成部分 字符范围 ASCII
参数字节 0x30-0x3F 0-9:;< = >?
中间字节 0x20-0x2F 空格、!"#$%&'()*+,-./
最终字节 0x40-0x7E @A-Z[\]^\_a-z{|}~

控制颜色

用案例引入

printf("\e[30;47m这是黑字白底。\e[0m\n");
printf("\e[1;34m这是粗体蓝色的文字。\e[0m\n");

在第一条语句中\e[表示启动 CSI 转义,30(参数字节)表示设置字体为黑色,47(参数字节)表示设置白底,m(最终字节)表示处理颜色,\e[0m0表示重置所有状态

第二条语句中,1表示粗体,34表示蓝色

样式参数

  • 1:粗体/高亮

  • 2:减弱

  • 3:斜体

  • 4:下划线

  • 7:反向显示

常用的颜色参数如下

图一:颜色

控制光标

  • \e[r;cH/\e[r;cf:将光标移动到整个终端的第r行第c列(绝对位置)

  • \e[H:光标移动到左上角

  • \e[NA:光标上移N

  • \e[NB:光标下移N

  • \e[NC:光标右移N

  • \e[ND:光标左移N

  • \e[?25l:隐藏光标

  • \e[?25h:显示光标

比如可以实现一个加载动画(旋转)的效果

#include <stdio.h>
#include <unistd.h>

int main()
{
    const char *spin = "|/-\\";
    printf("\033[?25l"); // 隐藏光标

    for (int i = 0; i < 20; i++)
    {
        // %c 打印字符,\r 回到行首
        printf("\r正在加载 %c ", spin[i % 4]);
        fflush(stdout);
        usleep(100000);
    }

    printf("\r加载完成!  \n");
    printf("\033[?25h"); // 恢复光标
    return 0;
}

清屏与清行

  • \e[2J:清空屏幕(可以用 \e[H\e[2J 清除屏幕内容并设置光标)

  • \e[K:清除行光标位置到行末尾的内容

  • \e[2K:清除当前行(光标不会回到行首,可以用 \r 使光标回到行首)

综合运用

1. 进阶进度条

#include <stdio.h>
#include <unistd.h>

int main()
{
    int total = 50;

    // 隐藏光标
    printf("\033[?25l");
    printf("正在同步数据...\n");

    for (int i = 0; i <= total; i++)
    {
        float percentage = (float)i / total * 100;

        // 清除当前行
        printf("\r\033[K");

        // i < 20 用红色 (31),i < 40 用黄色 (33),完成用绿色 (32)
        if (i < 20)
            printf("\033[31m");
        else if (i < 40)
            printf("\033[33m");
        else
            printf("\033[32m");

        printf("[");
        for (int j = 0; j < i; j++)
            printf("█");
        for (int j = 0; j < total - i; j++)
            printf(" ");
        printf("] %.1f%%", percentage);

        // 刷新缓冲区
        fflush(stdout);
        usleep(50000); // 50毫秒
    }

    printf("\n\033[32m✔ 同步完成!\033[0m\n");
    // 恢复光标
    printf("\033[?25h");
    // 回复颜色
    printf("\033[0m");

    return 0;
}

2. 终端弹球游戏

#include <stdio.h>
#include <unistd.h>
#define TRAIL_SIZE 5 // 拖尾长度

int main()
{
    const int width = 50, height = 15;
    int x = 1, y = 1, dx = 1, dy = 1;

    // 记录历史坐标的数组
    int history_x[TRAIL_SIZE] = {0};
    int history_y[TRAIL_SIZE] = {0};

    // 初始化:隐藏光标,清屏
    printf("\033[?25l\033[2J");

    for (int frame = 0; frame < 300; frame++)
    {
        // 擦除最旧的一个尾巴(变成空格)
        // 这里不用清屏是因为换成空格效率更高且效果更好
        if (history_x[TRAIL_SIZE - 1] != 0)
            printf("\033[%d;%dH ", history_y[TRAIL_SIZE - 1], history_x[TRAIL_SIZE - 1]);

        // 将历史坐标后移
        for (int i = TRAIL_SIZE - 1; i > 0; i--)
        {
            history_x[i] = history_x[i - 1];
            history_y[i] = history_y[i - 1];
        }
        history_x[0] = x;
        history_y[0] = y;

        // 更新当前球的位置
        x += dx;
        y += dy;
        if (x <= 1 || x >= width)
            dx = -dx;
        if (y <= 1 || y >= height)
            dy = -dy;

        // 绘制拖尾(颜色由深变浅)
        // 232-255 是灰度,数字越大越亮
        int colors[TRAIL_SIZE] = {250, 245, 240, 236, 233};
        for (int i = 0; i < TRAIL_SIZE; i++)
        {
            if (history_x[i] != 0)
            {
                // 5 表示缓慢闪烁
                printf("\033[%d;%dH\033[38;5;%dm·", history_y[i], history_x[i], colors[i]);
            }
        }

        // 绘制当前的球(高亮白色)
        printf("\033[%d;%dH\033[1;37mO\033[0m", y, x);

        fflush(stdout);
        usleep(40000); // 40ms 帧率更高更平滑
    }

    printf("\033[%d;1H\033[?25h游戏结束!\n", height + 1);
    return 0;
}

参考链接


C 语言中的格式化输出和转义字符
http://rotaxs.github.io/计算机科学/C语言/c-语言中的格式化输出和转义字符/
作者
Rotas
发布于
2025年10月22日
许可协议