特殊的转义字符—— \b 退格字符 ASCII 0x08

  • Post author:
  • Post category:其他




引入

我们在写 C 语言题目时,经常会碰见类似于

数字 分隔符 数字 分隔符 数字 分隔符

这样的输出。比如下面这段代码:

1=1
1+2=3
1+2+3=6
1+2+3+4=10

如果用循环的话,这个加号是个大问题。直接用

printf("%d+")

,最后面会多一个加号;用

printf("+%d")

则最前面会多一个加号。

想要解决,则必须判断当前输出的是否为第一个或者最后一个数字,然后做特殊处理。



新思路

有人就发现了,转义字符里有一个

\b

,这是个退格字符,能不能用它把多余的加号给删了呢?

那就试试呗,先输出个从 1 加到 5 试试

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int sum = 0;
	for (int i = 1; i <= 5; i++)
	{
		printf("%d+", i);
		sum += i;
	}
	printf("\b");
	printf("=%d\n", sum);
	return 0;
}

好像没什么毛病,对吧?

在这里插入图片描述

让我们再提交到判题平台上试试

在这里插入图片描述

加号居然没删掉,而且还多了个点出来!



真实含义

我们把上面的代码稍稍改动一下

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int sum = 0;
	for (int i = 1; i <= 5; i++)
	{
		printf("%d+", i);
		sum += i;
	}
	printf("\b");
	//printf("=%d\n", sum); //<---- 注释这一行
	return 0;
}

运行效果:

在这里插入图片描述

这段代码和上面的一模一样,只是把等号后面的输出给删掉了而已。


但是,最后的加号居然神奇地又出现了!

这是为什么呢?

我们先来看一下“退格”究竟为何含义。


\b

字符的确是退格字符,但此

退格非“删除”

。“退格”就是字面含义上的退格,即“往前退一格”,

相当于你在 Word 里按一下左方向键。

也就是说,


\b

并不能删除上一个字符,它只是把光标往前移了一下而已

那开头的代码为什么能正常输出呢?

很简单,

因为后面输出的字符覆盖掉了前面的字符,因此看起来好像是把上一个字符给删了。


为了更清晰的表示这个过程,我做了一个动图。(偷一下懒,图里只制作了三个数字求和,但原理是一样的。)

在这里插入图片描述

事实上退格键在早期打印机上的作用就是“往前退一格”,后来退格键的含义变了,变成了“往前退一格 + 删除一个字符”。



实际输出与显示

问题还没有完全解决:为什么在本地可以,但是上传到判题平台就不行了呢?


因为“显示的内容 ≠ 输出的内容”

请看以下代码:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	printf("123\n");
	printf("123\b\n");
	printf("123\b4\n");
	return 0;
}

运行结果

在这里插入图片描述

然而,我们把它编译,然后把程序的输出结果重定向到文件里,得到的结果是这样的:

123
123
1234

这段输出在不同的地方显示的内容可能不相同

Windows 记事本:一个框

Visual Studio 2015:啥也没有

在这里插入图片描述

Visual Studio 2019:一个带空心圈的实心框

![[Pasted image 20221125225938.png]]

Sublime:

![[Pasted image 20221125230132.png]]


可以看到我们的

\b

字符,也就是 ASCII 码为 0x08 的字符被原样输出了出来,

在文本编辑器里并没有实现退格的效果。

判题平台上使用的就是类似的方法,把程序的输出直接导出,传到网站上显示,但浏览器可不认

\b

,于是就显示为了一个红点。



实际应用

利用这个退格字符,我们可以做一个进度条出来

第一种:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main()
{
	int index = 0;
	char ch[] = {'|', '\\', '-', '/'};
	while (1)
	{
		putchar(ch[index]);

		index++;
		if (index >= 4)
			index = 0;

		Sleep(200); // Sleep(200) 的作用是延时 200 毫秒(0.2 秒)再继续执行下面的代码
		putchar('\b');
	}
	return 0;
}

演示

在这里插入图片描述

第二种:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main()
{
	//假设要做一个耗时较长的操作
	//为了更好的用户体验,我们需要一个进度条
	
	double progress = 0.1; //当前进度
	int length = 15; //进度条字符长度

	for (progress = 0.1; progress <= 1; progress += 0.05)
	{
		//先输出 length 个 \b,把光标倒到开头去
		//也可以直接用一个 \r
		for (int j = 0; j < length + 2; j++)
			putchar('\b');

		putchar('[');
		//已经完成部分的进度条
		int count = (int)(length * progress); 
		for (int j = 0; j < count; j++)
			putchar('#');
		//未完成部分的进度条
		for (int j = 0; j < length - count; j++)
			putchar(' ');
		putchar(']');
		
		Sleep(100);
	}
	return 0;
}

演示

在这里插入图片描述



版权声明:本文为XcantloadX原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。