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

【擂台赛】将字符串转换为ascii值的高效代码 -- 批处理完成

本帖最后由 plp626 于 2012-3-2 02:13 编辑

问题背景:密码存储的问题,以及编码存储的问题产生的。。。
该问题自己完成顿觉吃力,且不甚满意,故放在论坛,求助众人智慧
  1. :: 版本一,支持字母,数字,下划线
  2. set str=0123_ABCDXYZabcdxyz
  3. rem 代码?
  4. echo %ans%
复制代码
则输出48 49 50 51 95 65 66 67 68 88 89 90 97 98 99 100 120 121 122
  1. :: 版本二,支持尽量多的字符(0x20~0x7f)
  2. setlocal enabledelayedexpansion
  3. set str= ^^!"$%%&'()*+,-./09:;<=>?@AZ[^_`az|}~
  4. rem 代码?
  5. echo %ans%
复制代码
则输出32 33 34 36 37 38 39 40 41 42 43 44 45 46 47 48 57 58 59 60 61 62 63 64 65 90 91 95 96 97 122 124 125 126
------------------------------------------------------------------
为方便比较:
测试代码以执行一次@echo off为参照,计算代码耗时比值,从而大略消除因为各人系统配置不同而带来的耗时比较问题

代码要求
没有要求,
尽管可以掉三方,外部命令,只要能在循环体内很节省时间地调用(就只怕得不偿失)
为方便统一比较效率,强烈建议使用统一测试代码;

测试代码如下:
  1. @echo off
  2. :: 获得100000次执行 @echo off 任务的耗时,存放在ct1变量内
  3. set t1=%time%
  4. for /l %%a in (1 1 100000)do @echo off
  5. set t2=%time%
  6. call:etime t1 t2 tc1
  7. :: 初始工作(该处可省略),比如定义一些变量等,以便后面高效执行你的代码
  8. rem 。。。。
  9. :: 获得500次执行“你的代码”任务的耗时,存放在ct2变量内
  10. set t1=%time%
  11. for /l %%a in (1 1 500)do (
  12.     set str=0123_ABCDXYZabcdxyz
  13.     rem 版本一代码(或者版本二代码,但相应的str值请更换)
  14.    echo %ans%
  15. )
  16. set t2=%time%
  17. call:etime t1 t2 tc2
  18. :: 计算执行一次“你的代码”与执行一次“@echo off”的耗时比
  19. set/a rate=200*tc2/tc1
  20. echo 一次任务与一次“@echo off命令”耗时比=%rate%
  21. pause
  22. :etime <begin> <end> <ret> //求时差
  23. setlocal enabledelayedexpansion
  24. Set/a "c=(!%2:~,2!-!%1:~,2!)*360000+(1!%2:~3,2!-1!%1:~3,2!)*6000+1!%2:~-5,2!!%2:~-2!-1!%1:~-5,2!!%1:~-2!,c+=-8640000*(c>>31)"
  25. endlocal&set %3=%c%&goto:eof
  26. :: 某些标签子过程,(该处可省略)
  27. :label
  28. rem ....
复制代码
建议大家先别贴自己代码,先贴代码运行后的比值
请连续运行3次以上,多次求平均值以减小误差(比值越小自然代码效率越高),最后再亮代码,这样很有意思(代码别太长)

本帖最后由 neorobin 于 2014-10-17 22:59 编辑

回复 49# CrLf

不是 d

= 和 d 之间有一个退格符, 在论坛上不可见, 但可以用 点击 论坛的 复制代码 正常拷贝

不用不可见字符, 下面这样也可以, 0x20--0x7E 内的字符都不会被看作 eol 字符
  1. for /f delims^=^ eol^= %%j in ("!var!") do ...
复制代码

TOP

本帖最后由 CrLf 于 2014-10-17 20:37 编辑

回复 47# neorobin


"eol=delims=" 设置的是 eol=d,可以这样:
  1. for /F "tokens=2,3* delims=,"eol^= %%a in .....
复制代码

TOP

回复 47# neorobin
谢谢neorobin 指教了
对于 ";" 问题记得以前也讨论过处理   有时候用 FINDSTR /n 处理文件
当然设置 eol 也不失为一好的方法
唉 总是犯此类错
就此题的话 原方法不变的话 修改FOR为下面
  1.     for /f "tokens=1*" %%j in ("$ !str:~%%i,1!") do (
  2.         if "%%k" neq "" (
  3.            if "!#%%k!" neq "" (
  4.               if "!#%%k!" lss "%%k" (set Ans=!Ans! %%k-!$%%k!) else set Ans=!Ans! %%k-!$#%%k!
  5.            ) else if "!$%%k!" neq "" (set Ans=!Ans! %%k-!$%%k!)else set Ans=!Ans! %%k-61
  6.         ) else set Ans=!Ans! %%k-33
  7.     )
复制代码

TOP

本帖最后由 neorobin 于 2014-10-17 17:29 编辑

回复 40# terse

有一个问题, 分号 : 不能处理

我做了以下测试  (WinXP 32bit 简体中文版 和 Win7 64bit 简体中文版)
  1. @echo off
  2. echo 1 & for /f "delims=" %%a in (";") do echo %%a
  3. echo 2 & for /f "delims=" %%a in (";.") do echo %%a
  4. echo 3 & for /f "delims=" %%a in (".;") do echo %%a
  5. echo 4 & for /f "delims=" %%a in (".;.") do echo %%a
  6. pause
复制代码
得到输出为
  1. 1
  2. 2
  3. 3
  4. .;
  5. 4
  6. .;.
复制代码
我认为是 for /f 把分号 ;  当成了默认的行注释符(以此字符在行首, 将略过一行)

微软官方文档 eol 字符的解释, 我不认同
eol=c    Specifies an end of line character (just one character).

http://technet.microsoft.com/en-us/library/bb490909.aspx

但是这里的描述与事实是相符的
for /F "eol=; tokens=2,3* delims=," %i in (myfile.txt) do @echo %i %j %k
This command parses each line in Myfile.txt, ignoring lines that begin with a semicolon and passing the second and third token from each line to the FOR body (tokens are delimited by commas or spaces).

我尝试用 0x20--0x7E 范围之外的字符来做这个默认的 eol 字符, 比如 0x08,  0x20--0x7E 范围内字符便都可正常获取了, 测试代码如下:
  1. @echo off & mode 80,300
  2. set "ASCII_20_7E=                                 !"#$%%^&'()*+,-./0123456789:;^<=^>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^^_`abcdefghijklmnopqrstuvwxyz{^|}~"
  3. set ASCII_20_7E
  4. setlocal enabledelayedexpansion
  5. set /a cnt=0x20
  6. for /l %%i in (0x20 1 0x7e) do (
  7.     for /f "eol=delims=" %%j in ("!ASCII_20_7E:~%%i,1!") do (
  8.         if "%%j"=="!" echo ASCII-33
  9.         if "%%j"=="^" echo ASCII-94
  10.         echo !cnt!  [%%j]
  11.         set /a cnt+=1
  12.     )
  13. )
  14. pause
复制代码
1

评分人数

    • terse: 谢谢指正,&quot;;&quot; 应该存在技术 + 1

TOP

本帖最后由 terse 于 2012-3-6 18:53 编辑

回复 45# CrLf
还是不以为这样的流程 是取文件的16进制
我以为还应用等大空文件 不是吗?(当然这里不讨论这个话题了)

TOP

回复 44# terse


    因为楼主要求将保存为一行,所以不考虑超过变量上限的 hex 长度
如果要兼容大文件转 hex,那就加点料...
  1. @echo off
  2. copy /y /b %0+要测试的文件  tmp.tmp
  3. fc /b 要测试的文件 tmp.tmp>#
  4. setlocal enabledelayedexpansion
  5. for /f "eol=F skip=2 tokens=2" %%a in (#) do echo %%a
  6. pause
复制代码

TOP

回复 43# CrLf
我的意思是 假如临时文件$大于 CMD 应该出错的哦  当然此案另外

TOP

本帖最后由 CrLf 于 2012-3-6 18:21 编辑

回复 40# terse


    一直以来都极力避免使用变量索引表(生命游戏后遗症),不过看来是成见太深了...没想到,此处使用索引表所简化的流程用时竟足以弥补冗长的变量表所降低的效率。
    另:不知你在 37 楼提到的 “FC 方案 省去临时文件” 是指什么呢?如果是指可以写成 for /f "tokens=2" %%a in ('fc ...') do ... 的话,那么可以测试一下,理论上的用时会将会翻倍,因为 for /? 中提到,在 for /f  里从命令获取输出时, “该字符串会被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到内存中,并被当作文件分析。”


回复 42# plp626


    先清空变量环境再测试,这 4n:2n+3  的差异应该就会凸显出来(假设为变量名为单字符),而且在只以第一个等号为依据判断变量名的 xp 系统下估计会体现得更明显

TOP

回复 41# terse

是的;

看你的代码研究差别;发现定义100个变量值为单字符的变量和定义1个变量值为200个字符的变量;对set的影响差不多;

看来我低估了定义变量索引的思路。。

TOP

回复 39# plp626
测试代码取自一楼的 是如上面如此嵌入吗

TOP

  1. :::::::::::::::::::::::::::::::: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
  2. @echo off
  3. set /p var=<%0
  4. :: 获得100000次执行 @echo off 任务的耗时,存放在ct1变量内
  5. set t1=%time%
  6. for /l %%a in (1 1 100000)do @echo off
  7. set t2=%time%
  8. call:etime t1 t2 tc1
  9. setlocal enabledelayedexpansion
  10. for /l %%i in (32 1 126) do (
  11.    if defined _"!var:~%%i,1!" (
  12.       set "#!var:~%%i,1!=!var:~%%i,1!"
  13.       set "$#!var:~%%i,1!=%%i"
  14.    ) else set _"!var:~%%i,1!"=!var:~%%i,1!&set "$!var:~%%i,1!=%%i"
  15. )
  16. :: 获得500次执行“你的代码”任务的耗时,存放在ct2变量内
  17. set t1=%time%
  18. for /l %%a in (1 1 500)do (
  19. set str= ^^!"$%%&'()*+,-./09:;<=>?@AZ[^^_`az|}~
  20. set Len=0&set "Slen=!str!"
  21. for %%i in (4096 2048 1024 512 256 128 64 32 16)do if "!Slen:~%%i!" NEQ "" set/aLen+=%%i&set Slen=!Slen:~%%i!
  22. set Slen=!Slen!FEDCBA9876543210&set/aLen+=0x!Slen:~16,1!
  23. for /l %%i in (0 1 !Len!) do (
  24.     for /f "delims=" %%j in ("!str:~%%i,1!") do (
  25.         if "%%j" neq "" (
  26.            if "!#%%j!" neq "" (
  27.               if "!#%%j!" lss "%%j" (set Ans=!Ans! !$%%j!) else set Ans=!Ans! !$#%%j!
  28.            ) else if "!$%%j!" neq "" (set Ans=!Ans! !$%%j!)else set Ans=!Ans! 61
  29.         ) else set Ans=!Ans! 33
  30.     )
  31. )
  32. echo!Ans!&set Ans=
  33. )
  34. set t2=%time%
  35. call:etime t1 t2 tc2
  36. :: 计算执行一次“你的代码”与执行一次“@echo off”的耗时比
  37. set/a rate=200*tc2/tc1
  38. echo 一次任务与一次“@echo off命令”耗时比=%rate%
  39. pause
  40. :etime <begin> <end> <ret> //求时差
  41. setlocal enabledelayedexpansion
  42. Set/a "c=(!%2:~,2!-!%1:~,2!)*360000+(1!%2:~3,2!-1!%1:~3,2!)*6000+1!%2:~-5,2!!%2:~-2!-1!%1:~-5,2!!%1:~-2!,c+=-8640000*(c>>31)"
  43. endlocal&set %3=%c%&goto:eof
  44. :: 某些标签子过程,(该处可省略)
  45. :label
  46. rem ....
复制代码
3

评分人数

    • neorobin: THANKS技术 + 1
    • CrLf: 看来我对变量索引的偏见太深了...PB + 10 技术 + 1
    • plp626: 我的xp效率比900+;最终变量法最快速技术 + 1

TOP

回复 37# terse


    terse 能否发下你的700+的测试源代码?

TOP

本帖最后由 plp626 于 2012-3-5 21:11 编辑

那个win7运行下,看看结果,我这里比值是terse的快两倍了,rate=1300+
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. :: 获得100000次执行 @echo off 任务的耗时,存放在ct1变量内
  4. set t1=%time%
  5. for /l %%a in (1 1 100000)do @echo off
  6. set t2=%time%
  7. call:etime t1 t2 tc1
  8. :: 初始工作(该处可省略),比如定义一些变量等,以便后面高效执行你的代码
  9. call ascmap.cmd @
  10. set @=!@:~1,127!
  11. ::获取256查表法#ff表
  12. set $f= 0 1 2 3 4 5 6 7 8 9 a b c d e f&set #ff=&for %%a in (!$F!)do set #ff=!#ff!!$f: =%%a!
  13. :: 获得500次执行“你的代码”任务的耗时,存放在ct2变量内
  14. set t1=%time%
  15. for /l %%? in (1 1 500)do (
  16.     set str= ^^!"$%%&'()*+,-./09:;<=>?@AZ[^_`az|}~
  17. rem /***************** 字符范围0x01~0x7f ***********************
  18.     set ans=
  19.     for /l %%A in (0 1 33)do (
  20.         set #1=!str:~%%A,1!
  21.         set $1=
  22.         for /f "delims=%@:~97,26%" %%a in (":!#1!")do if %%a==: set $1=32
  23.         if !#1!==!@:~9^,1! (
  24.             set ##=10
  25.         )else if !#1!==^= (
  26.             set ##=61
  27.         )else if !#1!==^^! (
  28.             set ##=33
  29.         )else for /f "eol=!delims=" %%b in ("!#1!")do (
  30.             set ##=!#ff!!@:*%%b=!!@:*%%b=!&set/a##=128-0x!##:~-510,2!+$1
  31.         )
  32.         set ans=!ans! !##!
  33.     )
  34. rem *******************************************************/
  35.     echo !ans!
  36. )
  37. set t2=%time%
  38. call:etime t1 t2 tc2
  39. :: 计算执行一次“你的代码”与执行一次“@echo off”的耗时比
  40. set/a rate=200*tc2/tc1
  41. set rate
  42. pause
  43. :etime <begin> <end> <ret> //求时差
  44. setlocal enabledelayedexpansion
  45. Set/a "c=(!%2:~,2!-!%1:~,2!)*360000+(1!%2:~3,2!-1!%1:~3,2!)*6000+1!%2:~-5,2!!%2:~-2!-1!%1:~-5,2!!%1:~-2!,c+=-8640000*(c>>31)"
  46. endlocal&set %3=%c%&goto:eof
复制代码

TOP

本帖最后由 terse 于 2012-3-5 18:48 编辑

怪不得 测试值这么高 原来没清空Len变量 我就说 从代码流程来说不应该这么高撒 现在测试值我这里700多

另:CrLf版主的 FC 方案 省去临时文件?
1

评分人数

TOP

返回列表