[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

【练习-064】批处理将自身文本乱序输出

本帖最后由 batman 于 2011-4-21 22:25 编辑

出题目的:
  考查文本处理技巧;
  复习变量赋值法;
  加深对%0参数概念的理解。
加分原则:
  满分20分,视情况加分(以思路为重)。
解题要求:
  代码简洁、高效,在本题中不应生成临时文件;
  老手们暂缓出手。
题目如下:
  1、要求编写批处理将下面五行文字置于代码中,并将这五行共40个文字乱序后仍按每行8个字输出(可先输出到a.txt);
  我们去北大踢球吧
  快上那儿等着信号
  你不能离开大门口
  生与死就在一瞬间
  青春已从身边溜走
  2、假如输出后的内容如下:
  口就间边号球在离
  吧那身大一等生大
  死不们溜走去开能
  着瞬从北青春快已
  我踢与门你上信儿
  要求编写批处理列出现在每个文字相对原始文本中的偏移量(行偏移:列偏移),具体形式如死字原始位置为4:3,现在为3:1,偏移量则为-1:-2,而口字的偏移量则为-2:-7,各偏移量的输出用空格格开,一样每行8个。
补充:
  两步可写在一个批内,也可分步写成两个批。
----------------------------------------------
由于本人的失误原始文本中出现了两个边字,现已经改正(其中一个改为了儿字)。。。
***共同提高***

本帖最后由 neorobin 于 2011-4-25 01:47 编辑

各位的算法, 我没有仔细研究, 粗略看了下发现大家都用了些一样的技巧或方法,
我写的有些长, 想了下, 下面两段
  1. for /l %%i in (40 -1 1) ...
  2. for %%i in (!ind!) ...
复制代码
可以整合在一起, ind 和 cnt 两个变量省去了, (另外用位运算对 8 求商和余数只是看起来酷点呵, 没什么实质):
  1. for /l %%i in (0 1 39) do (
  2.   set /a "rem=%%i&7, rand=!random!%%(40-%%i)*4
  3.   for %%p in (!rand!) do set get=!nums:~%%p,3!
  4.   set /a "val=get&0xff, lineOffs=(%%i>>3)-(val>>3), colOffs=(%%i&7)-(val&7)
  5.   set "offs=!offs!!lineOffs!:!colOffs! "
  6.   for %%v in (!val!) do set new=!new!!str:~%%v,1!
  7.   if #!rem!==#7 set new=!new!!nl!& set offs=!offs!!nl!
  8.   for %%a in (!get!) do set nums=!nums:%%a,=!
  9. )
复制代码

TOP

33# terse
确实,忽略了


27# neorobin

测试环境中文编码未处理好, 以下用40个英文字母替代中文文本echo off
setlocal enableDelayedExpansion
set nl=^


for /l %%i in (0x100 1 0x127) do set nums=!nums!%%i,
for /l %%i in (40 -1 1) do (
  ...
neorobin 发表于 2011-4-23 00:45

那天测试的时候出错,后来发现是我不小心删了最后一个字符...
今天仔细研究了一下,虽然代码长,但是效率呱呱的,原因是通过将待随机取值的可用序号定义为一条长变量(这招太漂亮了),进而将输出的文字、偏移量各自定为一个长变量,创造出其他算法所无法做到的“干净环境”。而且在for /l 中以八进制、十六进制表示数字的用法我以前想都没想过,真是长见识了,这个新鲜技巧稍加磨砺,必定又是一柄利器。

TOP

29# zm900612
zm900612兄简化后的代码 可以再减少个FOR  两个并一个既可

TOP

本帖最后由 zm900612 于 2011-4-23 15:13 编辑

31# terse


算法问题,用call的初衷是解决for参数中不能使用变量的弊端。
不过刚刚想起来,如果不存在重复字符,就可以直接用变量替换而无需用for截取了

TOP

若给定文本中不存在重字 还是一个FOR里处理就好了 过多CALL 效率有问题
当然 作为另一思路则别论

TOP

偏移量我就不计算了,那个与上述代码八字不合,写进来比较影响效率和美观

TOP

本帖最后由 zm900612 于 2011-4-24 11:29 编辑

若给定文本中不存在重字,这样比原始的40次变量替换更快,而且random的范围更加合理:
  1. @echo off&setlocal enabledelayedexpansion&echo %time%
  2. for /f "useback skip=11" %%a in ("%~0") do set "str=!str!%%a"
  3. set str=@!str!
  4. for /l %%a in (0,1,16) do (
  5.     set /a a=!random!%%12+1
  6.     for %%b in (!a!) do call :str !str:~%%b,5!
  7. )
  8. for /l %%a in (1 8 33) do echo !str:~%%a,8!
  9. echo %time%&pause>nul
  10. :str
  11. for /f "tokens=1* delims=%1" %%a in ("!str!") do set "str=%%a!str:*%1=!%1"&exit /b
  12. 我们去北京踢球吧
  13. 快上那儿等着信号
  14. 你不能离开大门口
  15. 生与死就在一瞬间
  16. 青春已从身边溜走
复制代码
修改了“北大”为“北京”,16次call*5个字符的偏移范围,至少能把这40个字符循环两遍,保证随机性,同时random的取值范围降低,保证各部分字符串被随机选中的概率分布更为合理。
总之在效率和合理性上做了比较大的改进,相比原算法,最大的缺点就在于不支持重复字


现在发现,这似乎就是caruko说的“洗牌”算法,而且terse兄指出我绕弯了,其实只需要优化batman的代码就可以了...纯随机输出无重字文本时,这个大概是极限了(简化后的batman变量替换算法):
  1. @echo off&setlocal enabledelayedexpansion&echo %time%
  2. for /f "skip=9 useback" %%a in ("%~0") do set "str=!str!%%a"
  3. for /l %%a in (0,1,40) do (
  4. for %%b in (!random:~-1!) do (
  5. for /f %%c in ("!str:~%%b,1!") do set str=!str:%%c=!%%c
  6. )
  7. )
  8. for /l %%a in (0 8 32) do echo !str:~%%a,8!
  9. echo %time%&pause>nul
  10. 我们去北京踢球吧
  11. 快上那儿等着信号
  12. 你不能离开大门口
  13. 生与死就在一瞬间
  14. 青春已从身边溜走
复制代码
另有简化后的变量偏移的算法,效率和变量替换相差无几,支持重复字,但是代码多了两行:
  1. ;@echo off&setlocal enabledelayedexpansion&echo %time%
  2. ;for /f "useback" %%a in ("%~0") do set str=!str!%%a
  3. ;for /l %%a in (40 -1 1) do (
  4. ;        set /a "ra=!random!%%%%a",n=ra+1
  5. ;        for /f "tokens=1* delims=@" %%b in ("!ra!@!n!") do (
  6. ; set echo=!echo!!str:~%%b,1!
  7. ; set "str=!str:~0,%%b!!str:~%%c!"
  8. ; )
  9. ;)
  10. ;for /l %%a in (0 8 32) do echo !echo:~%%a,8!
  11. ;echo %time%&pause>nul&exit
  12. 我们去北大踢球吧
  13. 快上那儿等着信号
  14. 你不能离开大门口
  15. 生与死就在一瞬间
  16. 青春已从身边溜走走
复制代码
caruko提出的洗牌法,优点是可以轻易控制复杂程度和用时,缺点是经常洗不干净:
  1. ;@echo off&setlocal enabledelayedexpansion&echo %time%
  2. ;for /f "useback" %%a in ("%~0") do set str=!str!%%a
  3. ;for /l %%a in (1 1 40) do (
  4. ; set /a ra=!random:~-1!+5
  5. ; for %%b in ("!ra!") do (
  6. ; set str=!str:~-%%~b!!str:~5,-%%~b!!str:~0,5!
  7. ; )
  8. ;)
  9. ;for /l %%a in (0 8 32) do echo !str:~%%a,8!
  10. ;echo %time%&pause>nul&exit
  11. 我们去北大踢球吧
  12. 快上那儿等着信号
  13. 你不能离开大门口
  14. 生与死就在一瞬间
  15. 青春已从身边溜走
复制代码
以上几种算法都存在不易计算偏移量的先天缺陷,单独乱序输出时表现不错,但是若要同时计算偏移量,耗时将暴涨,此贴中另外三种在这方面则有优势:
常规算法就不提了,效率一般,也没什么艺术性。
读取自身输出作为输入的方案感觉已经无法再改进了,请见24楼terse代码。
最牛的应该是27楼neorobin的算法了,干净利落,浑然天成。

TOP

本帖最后由 zm900612 于 2011-4-23 22:23 编辑

16# batman

纯粹用变量替换存在重字bug(有两个“大”),我改进了下,但是用太多call,效率恐怕很低,感叹一下,要是for的参数能带变量该多好啊...
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "useback skip=10" %%a in ("%~0") do set "str=!str!%%a"
  3. set str=@!str!
  4. for /l %%a in (0,1,39) do (
  5.     for %%b in (!random:~-1!) do call :str !str:~%%b,1!
  6. )
  7. for /l %%a in (1 8 33) do echo !str:~%%a,8!
  8. pause>nul
  9. :str
  10. for /f "tokens=1* delims=%1" %%a in ("!str!") do set "str=%%a!str:*%1=!%1"&exit /b
  11. 我们去北大踢球吧
  12. 快上那儿等着信号
  13. 你不能离开大门口
  14. 生与死就在一瞬间
  15. 青春已从身边溜走
复制代码
修改了下漏洞

TOP

测试环境中文编码未处理好, 以下用40个英文字母替代中文文本:
  1. @echo off
  2. setlocal enableDelayedExpansion
  3. set nl=^
  4. for /l %%i in (0x100 1 0x127) do set nums=!nums!%%i,
  5. for /l %%i in (40 -1 1) do (
  6.   set /a rad=!random!%%%%i*4
  7.   for %%p in (!rad!) do (
  8.     set get=!nums:~%%p,3!
  9.     set /a val=get"&0xff
  10.     set ind=!ind!!val!,
  11.     for %%a in (!get!) do set nums=!nums:%%a,=!
  12.   )
  13. )
  14. for /f "usebackq delims=" %%l in ("%~0") do (
  15.   if /i "%%l"==":endText" set getText=0
  16.   if $!getText!==$1 set str=!str!%%l
  17.   if /i "%%l"==":text" set getText=1
  18. )
  19. for %%i in (!ind!) do (
  20.   set new=!new!!str:~%%i,1!
  21.   set /a lineOffs=cnt/8-%%i/8,colOffs=cnt%%8-%%i%%8
  22.   set "offs=!offs!!lineOffs!:!colOffs! "
  23.   if #!rem!==#7 set new=!new!!nl!& set offs=!offs!!nl!
  24.   set /a cnt+=1,rem=cnt%%8
  25. )
  26. echo !new!
  27. echo !offs!
  28. pause
  29. exit
  30. :text
  31. abcdefgh
  32. ijklmnop
  33. qrstuvwx
  34. yzABCDEF
  35. GHIJKLMN
  36. :endText
复制代码
1

评分人数

    • zm900612: 好!原来可以这么用...技术 + 1

TOP

本帖最后由 terse 于 2011-4-22 17:51 编辑

变量太多影响效率  练手了
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f %%i in ('"find /c /v "" < %0"') do set/a n=%%i-5
  3. for /f "skip=%n% useback" %%i in ("%~0") do set str=!str!%%i
  4. for /l %%i in (0 1 39) do set i=10%%i&set var=!var!#!i:~-2!&set #!i:~-2!=%%i
  5. for /l %%i in (40 -1 1) do (
  6.     set /a "R=!random!%%%%i*3,N=(40-%%i)/8,M=(40-%%i)%%8"
  7.     for /f "tokens=1-3" %%a in ("!R! !N! !M!") do (
  8.         for %%j in ("!var:~%%a,3!") do for %%k in (!%%~j!) do (
  9.             set "var=!var:%%~j=!"
  10.             set /a "AN=%%k/8-%%b,AM=%%k%%8-%%c"
  11.             if %%c equ 7 (echo !#b!!str:~%%k,1!&set #b=) else set #b=!#b!!str:~%%k,1!
  12.             set _%%b=!_%%b! !AN!:!AM!
  13.         )
  14.     )
  15. )
  16. for /l %%i in (0 1 4) do echo!_%%i!
  17. pause&exit
  18. 我们去北大踢球吧
  19. 快上那边等着信号
  20. 你不能离开大门口
  21. 生与死就在一瞬间
  22. 青春已从身边溜走
复制代码

TOP

16# batman


原先我也用了一点变量替换,但是总是莫名其妙少掉一个字,后来发现给定的文本中含有两个“大”,所以变量替换会出错

TOP

前面回复直接 40 个ECHO 其实和17楼900612兄一样思路
  1. @echo off&setlocal enabledelayedexpansion
  2. if "%1"=="" (
  3.    set N=0
  4.    for /f "skip=15 useback" %%i in ("%~0") do set str!n!=%%i&set/a n+=1
  5.    for /f "tokens=2-4" %%a in ('%0 $^|sort') do (
  6.        set /a "N=L/8,M=L%%8,X=%%b-N,Y=%%c-M,L+=1"
  7.        for %%j in (!N!) do (
  8.            set _%%j=!_%%j! !X!:!Y!
  9.            if !M! equ 7 (echo !#%%j!%%a) else set #%%j=!#%%j!%%a
  10.        )
  11.    )
  12.        for /l %%i in (0 1 4) do echo!_%%i!
  13.        pause&exit
  14. ) else for /l %%i in (0 1 4) do for /l %%j in (0 1 7) do echo !random!!random! !str%%i:~%%j,1! %%i %%j
  15.   exit
  16. 我们去北大踢球吧
  17. 快上那边等着信号
  18. 你不能离开大门口
  19. 生与死就在一瞬间
  20. 青春已从身边溜走
复制代码

TOP

我写了这个测试代码
  1. @echo off&setlocal ENABLEDELAYEDEXPANSION
  2. for /l %%i in (1,1,10000) do set /a _%%i=%%i
  3. echo !time!
  4. for /l %%i in (1,1,10000) do set /a _1+=2
  5. echo !time!
  6. for /l %%i in (1,1,10000) do set /a _%%i+=2
  7. echo !time!
复制代码
同样是1万个变量
1:只读取一个变量进行计算,耗时6秒。
2:所有变量都读取进行计算,耗时16秒。

TOP

的确,变量的增加让批处理速度变慢,但好像变慢的是变量读取速度,在频繁读取变量的时候差距就越大。

原因猜想:
可能真的是因为变量有序存放的原因?类似数据库?
不像一般的程序语言机制,只是增加内存消耗,没有明显增加读取耗时。

TOP

返回列表