luliucheng 发表于 2022-11-25 20:17

控制台特殊字符实现光标控制&批处理中的小技术

## 前言
上一次涛之雨大佬已经在[这篇文章](https://www.52pojie.cn/thread-1708277-1-1.html)中讲解了彩色字符的打印方法:ANSI转义码~~[[维基](https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97)(内容已经多了一点了)]~~但事实上,ANSI转义码的用处不止于此。
## ANSI转义码还能做什么
ANSI转义码还能够修改文本样式,控制光标位置。比如,下面这张表列出了一些常用的并且**最新版Windows终端支持的**用于控制光标的ANSI转义码:(此表摘自前文给出的链接)

|代码|名称|作用|
|----|----|----|
|CSI `n` A|CUU – 光标上移(Cursor Up)|光标向指定的方向移动`n`(默认1)格。如果光标已在屏幕边缘,则无效。|
|CSI `n` B|CUD – 光标下移(Cursor Down)|同上
|CSI `n` C|CUF – 光标前移(Cursor Forward)|同上
|CSI `n` D|CUB – 光标后移(Cursor Back)|同上
|CSI `n` E|CNL – 光标移到下一行(Cursor Next Line)|光标移动到下面第`n`(默认1)行的开头。|
|CSI `n` F|CPL – 光标移到上一行(Cursor Previous Line)|光标移动到上面第`n`(默认1)行的开头。|
|CSI `n` G|CHA – 光标水平绝对(Cursor Horizontal Absolute)|光标移动到第`n`(默认1)列。|
|CSI `n;m` H|CUP – 光标位置(Cursor Position)|光标移动到第`n`行、第`m`列。值从1开始,且默认为`1`(左上角)。例如`CSI ;5H`和`CSI 1;5H`含义相同;`CSI 17;H`、`CSI 17H`和`CSI 17;1H`三者含义相同。|
|CSI `n` J|ED – 擦除显示(Erase in Display)|清除屏幕的部分区域。如果`n`是0(或缺失),则清除从光标位置到屏幕末尾的部分。如果`n`是1,则清除从光标位置到屏幕开头的部分。如果`n`是2,则清除整个屏幕。如果`n`是3,则清除整个屏幕,并删除回滚缓存区中的所有行。|
|CSI `n` K|EL – 擦除行(Erase in Line)|清除行内的部分区域。如果`n`是0(或缺失),清除从光标位置到该行末尾的部分。如果`n`是1,清除从光标位置到该行开头的部分。如果`n`是2,清除整行。光标位置不变。|
|CSI `n` S|SU – 向上滚动(Scroll Up)|整页向上滚动`n`(默认1)行。新行添加到底部。|
|CSI `n` T|SD – 向下滚动(Scroll Down)|整页向下滚动`n`(默认1)行。新行添加到顶部。|

还有一些转义码用于控制字体样式:

|代码|作用|备注|
|----|----|----|
|0|重置/正常|关闭所有属性。
|1|粗体/增加强度/高亮|
|2|弱化(降低强度)|
|3|斜体|
|4|下划线|
|5|闪烁|
|7|反显|前景色与背景色交换。
|8|隐藏|
|9|划除|加上删除线。
|22|正常颜色或强度|不强不弱。
|23|非斜体|
|24|关闭下划线|
|25|关闭闪烁|
|27|关闭反显|
|28|关闭隐藏|
|29|关闭划除|

通过这些转义字符,我们可以制作一个极富艺术性(bushi)的批处理。
## 批处理中的小技术
要制作这样的批处理,首先要生成`ESC`字符。可以使用以下代码:
```
for /f %%a in ('echo prompt $E ^| cmd') do set ESC=%%a
```
然后在代码中可以使用`%ESC%`表示`ESC`字符。
批处理中还需要生成随机数,我使用了类似以下的命令:
```
set /a randout=%random%*随机数的最大值/32767
```
为什么这样写而不是直接取`%random%`的余数呢?因为直接取余得到不同数的概率不相等,这与C/C++中直接`rand()%n`存在的问题是一样的。
另外,为了调用方便,适当使用**函数/子程序**也能提高编程效率。怎么写子程序?很简单:
```
echo 调用子程序:
call :name
REM 主程序到此结束,批处理退出:
exit /B

REM 下面是子程序部分
:name
REM name是一个标签,可以标注代码中的跳转点,
REM 也相当于子程序的起点,你可以把它换成你要的名字
echo Hello!
exit /B
REM 上一行指令相当于return,批处理没有子程序结束标记,
REM 必须用exit /B退回主程序。
```
程序中还使用`set /p`显示提示信息不换行的特性实现了文本输出不换行:
```
<nul set /p _dev_stdout=输出文本
```

## 批处理源码
#### 注意:多数文本编辑器默认以UTF-8编码保存文件,请在保存批处理时将其以ANSI编码保存!!!
终端的上半部分随机填充方块是使用光标位置控制序列实现的,最底下的一行文本是使用字体样式控制序列实现的。
```
@echo off

REM 生成ESC字符
for /f %%a in ('echo prompt $E ^| cmd') do set ESC=%%a

set cnt=100
:main
set out=%ESC%[
call :randposY
set out=%out%%randout%;
call :randposX
set out=%out%%randout%H%ESC%[48;2;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%m%ESC%[0m
<nul set /p _dev_stdout=%out%

REM 每循环10次显示一次新的底部文本
REM 不加判断文本刷新太快,效果很差
set /a cnt=cnt+1
if %cnt% lss 10 (goto main)

call :randstyle
set out=%ESC%[30;1H%ESC%[%randout%m%ESC%[38;2;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%m%ESC%[48;2;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%m批处理精神污染
call :randstyle
set out=%out%%ESC%[%randout%m%ESC%[38;2;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%m%ESC%[48;2;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%;
call :randcolor
set out=%out%%randout%mBy 吾爱破解论坛 luliucheng%ESC%[0m
<nul set /p _dev_stdout=%out%
set cnt=0
goto main

REM 各种子程序
:randposX
set /a randout=%random%*60/32767*2
exit /B

:randposY
set /a randout=%random%*30/32767
exit /B

:randcolor
set /a randout=%random%/128
exit /B

:randstyle
set randsource=123479
set /a randout=%random%*6/32767
setlocal ENABLEDELAYEDEXPANSION
set randout=!randsource:~%randout%,1!
endlocal
exit /B
```

semiuel 发表于 2022-11-25 21:00

win7 64位,运行后输出内容没有转义

Wapj_Wolf 发表于 2022-11-26 08:50

看着就高级,谢谢分享,收藏了慢慢消化。

yuzzuo 发表于 2022-11-26 18:39

高级的一批

luliucheng 发表于 2022-11-26 20:11

semiuel 发表于 2022-11-25 21:00
win7 64位,运行后输出内容没有转义

是的,转义序列需要win10系统才能支持。

eaglexiong 发表于 2022-12-2 11:25

学习编程语言要专业,开发算法会提升很多
页: [1]
查看完整版本: 控制台特殊字符实现光标控制&批处理中的小技术