Board logo

标题: [分享]批处理多行回退(规律讨论篇) [打印本页]

作者: batman    时间: 2011-3-1 23:09     标题: [分享]批处理多行回退(规律讨论篇)

全文由本人同步发表在个人qq空间:http://user.qzone.qq.com/841615149/infocenter
  还记得刚刚接触批处理时很喜欢写特效,看着自己编写的代码在dos窗口中变成一幅幅跳动的画面时,心里那个成就感啊。。。可是,慢慢就发现用批写特效真的是很费劲,受到的限制太多了。其中最受不了的就是动画的实现基本靠cls刷屏重输出,整个画面闪动的厉害,一点都不连贯,这些都是因为dos的逐行输出性质决定的。但值得庆幸的是微软总算还是创造了一个神奇的退格符“”,用它和空格组合可以将最后行进行重输出而不用cls来刷屏,如下代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. set "str=  我在向不断向左移动哦"
  3. for /l %%a in (1,1,24) do set "t=!t! "
  4. echo 请注意下面文字的变化&echo.&set /p===^><nul
  5. :lp
  6. for /l %%a in (1,1,11) do (
  7.      set /a a=6*%%a
  8.      for %%b in (!a!) do set "t1=!t:~%%b!"
  9.      set /p=!str:~%%a!<nul
  10.      for /l %%b in (1,1,2000) do echo>nul
  11.      set /p=!t1!<nul
  12. )
  13. goto lp
复制代码
画面总算不闪而且流畅了,这都是神奇的“”的功劳啊。那么它是怎么实现这一效果的呢?“”的学名叫做退格符,但它这个退格是将光标(即输出点)退回一格,请记住它并不会删除前面的输出内容。但它和空格的组合“  ”就能成功将前一格的内容删除并将光标回退一格(大家想一想为什么能够实现)。在这里,我还要提醒大家退格每次只回退一个字节的距离,这个距离相当于单字节字符(数字、字母、字符)的宽度以及双字节字符(汉字、标点)的一半宽度。所以我们只要知道要回退删除的距离为n(以单字节计算),就可以用n个“  ”组合来实现精确的回退删除,如果要重输出整行,我们就可以采用足够多的“  ”组合来实现(而不用知道要回退的距离)。但是这种方法仅仅只能对末行进行重输出,如果要对末行以上的行进行重输出就只能用cls刷屏重输出了。当然,我们还可以用将一帧帧的画面先输出到临时文本中,cls后再用type对临时文本进行输出,如此来实现对画面的重写,而这个要比单单cls刷屏重写要显得流畅些(因为无论是多少内容,type都是一次性输出):
  1. @echo off&setlocal enabledelayedexpansion
  2. set "str=我是一帧帧的画面,连贯起来就成了动画"
  3. for /l %%a in (0,1,17) do (
  4.      echo !str:~%%a,1!>tmp
  5.      type tmp
  6.      for /l %%b in (1,1,2000) do echo>nul
  7.      cls
  8. )
  9. del /q tmp&pause
复制代码
但是,当输出的内容很多时,用type方法效率上就有问题了。而且很多时候我们仅仅是对画面的局部进行变化,如果每次要输出整个画面确实是一种效率上的浪费(个人认为)。那么,还有没有别的办法来实现对画面局部变化呢?前面我特意提到神奇的“”,如果仅仅局限在一行内实现回退是称不上神奇的。那么它真正的神奇之处在哪里呢?一个偶然的机会,本人发现用echo输出一个tab+n个退格符就能将光标回退到原点,注意这个n是足够多,本人当时取的是1000,就在论坛发表了以下代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. :: 灵感来源于cn-dos趣味东的多行回退
  3. :: 趣味东发现用set /p输出一个tab+n个退格就将光标回退多行,但并不好控制
  4. :: 于是本人就想到是不是能将光标回退到原点(屏幕左上角)
  5. :: 结果发现用echo输出一个tab+n个退格就能将光标退回到原点过一格的位置,但有错误信息输出
  6. :: 于是,用2>nul屏蔽错误信息,并再加一个退格将光标退回原点
  7. :: 下面通过代码进行简单演示,至于原因本人暂未搞明白
  8. ::请将下面的tab换成实际的制表符
  9. set "t=tab"
  10. title 神奇的回退
  11. for /l %%a in (1,1,1000) do set "k=!k!"
  12. for /l %%a in (1,1,10) do echo ○○○○○○○○○○
  13. ping /n 2 127.1>nul
  14. :: 这里将光标退回原点处,请仔细注意光标的位置
  15. echo %t%%k% 2>nul&set /p=<NUL
  16. pause>nul
  17. :: 请注意6-10行第6-10个字符的输出是没有改变的
  18. for /l %%a in (1,1,5) do echo ●●●●●¤¤¤¤¤
  19. for /l %%a in (1,1,5) do echo ⊙⊙⊙⊙⊙
  20. pause>nul
复制代码
这确实是一个重大的突破,但是按这个方法将光标退回到原点后再用echo,方法可以将光标重定位到要修改的行的行首(仅能到定位到行首),就能方便地实现画面的局部修改。但本人在一次测试时(当时是输出的150行)发现光标没有按计划退到原点,而是只是回退了99行。这下看来这个光标回退行是有规律的,于是,本人写下了下面的测试代码来寻找这一规律(当时猜想应该是个10*行+n的规律):
  1. @echo off&setlocal enabledelayedexpansion
  2. ::请将下面的tab换成实际的制表符
  3. set "t=tab"
  4. for /l %%a in (1,1,210) do (
  5.      set "k=!k!"&cls
  6.      for /l %%b in (1,1,20) do (
  7.          for /l %%c in (1,1,20) do set "str=!str!■"
  8.          echo !str!&set "str="
  9.       )
  10.       2>nul echo %t%!k!
  11.       echo %%a
  12.       for /l %%b in (1,1,1000) do echo>nul
  13. )
  14. pause>nul
复制代码
通过这个测试,终于得出了计算公式,退格符的个数=要回退的行(含空行)*10+2。如此以来,我们就掌握了回退行的规律了,通过与echo,的配合就能随时实现光标的上上下下了(当然指在行首)哈哈,下面就小写一段特效用以“自诩”_|_":
  1. @echo off&setlocal enabledelayedexpansion
  2. mode con cols=50 lines=12&color 9f
  3. ::请将下面的tab换成实际的制表符
  4. set "t=tab"
  5. for /l %%a in (1,1,11) do (
  6.     set /p=     *<nul
  7.     for %%b in (1 11) do if %%a equ %%b set "flag=a"
  8.     if defined flag (
  9.        for /l %%b in (1,1,39) do set /p=*<nul
  10.        set "flag="
  11.        ) else (
  12.        if %%a equ 6 (
  13.           set /p=                无 为                *<nul
  14.           ) else (     
  15.           for /l %%b in (1,1,38) do set /p= <nul
  16.           set /p=*<nul
  17.        )
  18.     )
  19.     echo.
  20. )
  21. for /l %%a in (1,1,52) do set "k=!k!"
  22. 2>nul echo,%t%!k!&set "k=!k:~30!"
  23. for /f "tokens=3 delims=:" %%a in (%~0) do (  
  24.     set /a n+=1,m=n%%2
  25.     set /p=     *<nul
  26.     for /l %%b in (1,1,12) do set /p= <nul
  27.     set /p=%%a<nul
  28.     for /l %%b in (1,1,2000) do echo>nul
  29.     if !m! equ 0 (
  30.        2>nul echo,%t%%k%
  31.        ) else (
  32.        echo,&echo,
  33.     )
  34. )
  35. for /l %%b in (1,1,2000) do echo>nul
  36. 2>nul echo,%t%%k%
  37. echo      *               无 为
  38. for /f "tokens=3 delims=:" %%a in (%~0) do echo      *            %%a            
  39. pause>nul   
  40. ::#:#:光阴无声逝如风
  41. ::#:#:一梦醒觉行囊空
  42. ::#:#:可叹寒窗十年问
  43. ::#:#:不入尘缘竟峥嵘
  44. ::#:#:遥想子成家业时
  45. ::#:#:情迷书墨一老翁
复制代码
小诗也是自己乱写的,代码也没有简化,权当好玩了,呵呵。

【扩展资料】
探讨:ECHO;TAB若干退格字符将光标多行回退与窗口列宽的关系
http://bbs.bathome.net/thread-41792-1-1.html
作者: wc726842270    时间: 2011-3-2 08:46

版主的代码也让我明白了CMD是以16进制—双字节读取内容的。刚才试过保存为UTF—8和用ASC||字符(在ANSI的情况下应该用0补位了吧,所以也是2个字节),在退格符处理ANSI中的字符时有点小意外,不过这好像是处理补位的结果吧。不过还是希望有个明确的答案
是不是因为补位会被过滤啊,现在还仅仅是个猜测

[ 本帖最后由 wc726842270 于 2011-3-2 08:55 编辑 ]
作者: cjiabing    时间: 2011-3-2 10:00

batman 是个人才,谢谢分享,终于有人肯下功夫来研究这一特效了,这个功能将极大地促进批处理动画的发展,期待中!~
作者: Batcher    时间: 2014-12-26 16:41

test-1.bat 退格符直接写在脚本里面
  1. @echo off
  2. echo bbs.bathome.net
  3. set /p =光标回退(非cls清屏)动画效果演示:<nul
  4. for /l %%i in (1,1,10) do (
  5.     set /p =%%i<nul
  6.     set /p =<nul
  7.     ping -n 2 127.1 >nul
  8. )
  9. echo,
  10. pause
复制代码
test-2.bat 用命令生成退格符
  1. @echo off
  2. echo bbs.bathome.net
  3. for /f %%i in ('echo prompt $H ^| cmd') do (
  4.     set "KeyBS=%%i"
  5. )
  6. set /p =光标回退(非cls清屏)动画效果演示:<nul
  7. for /l %%i in (1,1,10) do (
  8.     set /p =%%i<nul
  9.     set /p =%KeyBS%<nul
  10.     ping -n 2 127.1 >nul
  11. )
  12. echo,
  13. pause
复制代码
test-3.bat 倒计时+不换行+空格开头
  1. @echo off
  2. echo   bbs.bathome.net
  3. for /f %%i in ('echo prompt $H ^| cmd') do (
  4.     set "KeyBS=%%i"
  5. )
  6. echo   倒计时:
  7. for /l %%i in (9,-1,1) do (
  8.     set /p "= %KeyBS%  %%i"<nul
  9.     set /p "=%KeyBS%%KeyBS%%KeyBS%"<nul
  10.     timeout /t 1 >nul
  11. )
  12. echo,
  13. echo   执行完毕
  14. pause
复制代码

作者: 老刘1号    时间: 2017-4-9 10:28

这么6的帖子没人顶不科学啊
作者: 1055367558    时间: 2017-9-24 10:47

诶呦,不错啊。。以前还不知道怎么用
  1. @echo off
  2. set /p =光标回退显示时间(非cls清屏):<nul
  3. :a
  4. set/p =%time:~0,8%<nul
  5. for /l %%k in (1,1,8) do (set/p=<nul)
  6. ping -n 1 127.1 >nul
  7. goto a
复制代码

作者: yyz219    时间: 2021-10-22 09:20

回复 5# 老刘1号
确实很强大。学习学习
作者: hnfeng    时间: 2023-3-14 11:30

厉害,强大啊
作者: dos-a    时间: 2023-4-3 19:53

回复 1# batman


    现在win10上无法实现多行回退啊




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2