[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]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次以上,多次求平均值以减小误差(比值越小自然代码效率越高),最后再亮代码,这样很有意思(代码别太长)

恩;
我想会有人在bat版问:vbs一行代码的事情为什么要多行去做?
答案在风中。。。

TOP

测试的字符串已经给范围了
^!"$%&'()*+,-./09:;<=>?@AZ[^_`az|}~

能处理这些就OK了

TOP

本帖最后由 plp626 于 2012-3-3 03:15 编辑

想到一个方法,对单一的字符:

先获取ascii表,比如存放在全局变量@中,(call ascmap.cmd @)

对= ! 这俩字符用if判断下单独处理

然后在获取!@:*%char%=!的长度(这个用查表法,很快),再用255减去这个长度变是char字符的ascii值了;
对于字母的大小写,可用for %%a in (!char!do if %%~dp neq %%a echo 小写
然后对于小写的把最后的索引值+32即可;
这个思路,无需逐一比较是否和ascii表对应字符相等,少去一重循环;

当在437代码也运行时;用ascii表的方法可以处理0x01~0xff的所有字符;
明天贴代码。。。

TOP

本帖最后由 plp626 于 2012-3-4 12:50 编辑

搞了个单字符转ascii索引值的专用函数,#char2asc;
中文代码页下运行,支持0x01~0x7f
英文代码页下运行,支持0x01~0xff
  1. @ECHO OFF
  2. setlocal enabledelayedexpansion
  3. :: 定义256表,存入#ff
  4. 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!
  5. :: 获取0x00~0x7f ascii表,存入@
  6. CALL ASCMAP.CMD @
  7. rem ------------------------- 内部 -----------------------------
  8. :: 定义#char2asc 函数,入口参数#1,出口参数##;外部依赖全局变量@={ascii码表0x00~0xff};#ff={查表法求字符串长之256位表}
  9. set "#char2asc=(set $1=&for /f "delims=%@:~97,26%" %%a in ("^^!#1^^!:")do if %%a==: set $1=32)&if ^!#1^!==^!@:~10^^,1^! (set ##=10)else for /f "delims==^^! " %%a in ("^^!#1^^!:")do if %%a==: (if "^^!#1^^!"=="=" (set ##=61)else if "^^!#1^^!"=="^^^^^^!" (set ##=33)else set ##=32)else for /f "eol= delims=" %%b in ("^^!#1^^!")do set #2=^!@:*%%b=^!&set $s=^!#ff^!^!#2^!^!#2^!&set/a##=255-0x^!$s:~-512,2^!+$1&if ⒈ lss 2 set/a ##-=63"
  10. :: 执行#char2asc函数,以字符A做测试
  11. set #1=A
  12. %#char2asc%
  13. echo ascii_code(!#1!)=!##!
  14. pause
复制代码

TOP

本帖最后由 plp626 于 2012-3-3 09:20 编辑
版本二:
一次任务与一次“@echo off命令”耗时比=1571
一次任务与一次“@echo off命令”耗时比=1463
用 ...
CrLf 发表于 2012-3-2 05:16



    你这个调用外部命令,效率还这么高?

我的测试了下,版本二目前耗时比2100+

你机子 调用一次"find/?"与执行一次"@echo off" 的耗时比?

我的环境 "find/?"与"@echo off" 耗时比=4281;
          "find/?"与"set a=#4647" 耗时比=2437;


我的机子环境:
  1. d:\Documents_and_Settings\plp2\desktop>cd.>f.t&cd.>g.t
  2. d:\Documents_and_Settings\plp2\desktop>cff.cmd "@echo off" "set a=1" "set a=^!random:~2,1^!"; "set a=^!random:2=#^!"; "set abcd12345678901234567890=$$"; "call set a=1234567890"; "call set/a a=2" "call set f=%%a%%"; popd "setlocal&endlocal"; "if a==0 ?"; "for %a in (1)do set b=%a"; "for /f %a in ("1")do set b=%a"; "ver>nul"; "echo off>a.t" "echo.>b.t 4>a.t" "echo.>b.t" "if exist xxxx.txt ?"; "for %a in (#.txt)do set x=%a"; rem; "copy/b f.t+g.t f.t>nul"; "copy f.t+g.t f.t>nul" shift "fc/? >nul" "findstr/? >nul" "find/? >nul"
  3. Microsoft Windows XP [版本 5.1.2600]
  4. Genuine Intel(R) CPU           T1600  @ 1.66GHz
  5. NUMBER_OF_PROCESSORS=2
  6. ------------ CMD指令测试 -------------
  7. "@echo off"                           @ 119870.7 Hz
  8. "set a=1"                             @  79261.2 Hz
  9. "set a=6"                             @  76784.3 Hz
  10. "set a=#4647"                         @  68252.7 Hz
  11. "set abcd12345678901234567890=$$"     @  58490.4 Hz
  12. "call set a=1234567890"               @    534.2 Hz
  13. "call set/a a=2"                      @   5670.3 Hz
  14. "call set f=%%a%%"                    @    566.6 Hz
  15. "popd"                                @ 106830.4 Hz
  16. "setlocal&endlocal"                   @   2934.6 Hz
  17. "if a==0 ?"                           @ 144550.0 Hz
  18. "for %a in (1)do set b=%a"            @  45492.5 Hz
  19. "for /f %a in ("1")do set b=%a"       @  47242.3 Hz
  20. "ver>nul"                             @   6124.0 Hz
  21. "echo off>a.t"                        @   1956.4 Hz
  22. "echo.>b.t 4>a.t"                     @    850.0 Hz
  23. "echo.>b.t"                           @    971.7 Hz
  24. "if exist xxxx.txt ?"                 @   9893.5 Hz
  25. "for %a in (#.txt)do set x=%a"        @  39622.5 Hz
  26. "rem"                                 @ 351067.8 Hz
  27. "copy/b f.t+g.t f.t>nul"              @    890.4 Hz
  28. "copy f.t+g.t f.t>nul"                @    667.8 Hz
  29. "shift"                               @ 136519.4 Hz
  30. "fc/? >nul"                           @     26.9 Hz
  31. "findstr/? >nul"                      @     28.0 Hz
  32. "find/? >nul"                         @     28.0 Hz
复制代码

TOP

本帖最后由 plp626 于 2012-3-3 10:41 编辑

费力不讨好,楼上上的代码#char2asc耗时不过是下面代码1/6~1/10,而消耗的空间代价可读性代价是惨重的;
  1. call ascmap.cmd @
  2. set #1=A
  3. for /l %%a in (0 1 127)do if /i "!@:~%%a,1!"=="!#1!" set ##=%%a
  4. set ##
复制代码
--------------------------------------

不过又想到一个二分法,把ascii表中的字符按if比较顺序排列,任意字符二分法7次可定位索引值,这和求字符串长度一样了;
  1. 带引号单字符
  2. 20 FF 09 0A 0B 0C 0D 21 01 02 03 04 05 06 07 08 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 7F 27 2D 22 23 24 25 26 28 29 2A 2C 2E 2F 3A 3B 3F 40 5B 5C 5D 5E 5F 60 7B 7C 7D 7E AD A8 2B F9 3C 3D 3E F1 AE AF F6 FB EF F7 F0 F3 F2 A9 F4 F5 FE C4 CD B3 BA DA D5 D6 C9 BF B8 B7 BB C0 D4 D3 C8 D9 BE BD BC C3 C6 C7 CC B4 B5 B6 B9 C2 D1 D2 CB C1 CF D0 CA C5 D8 D7 CE DF DC DD DE DB B0 B1 B2 9B 9C 9D AA F8 E6 FA 9E 30 AC AB 31 32 FD 33 34 35 36 37 38 39 EC 61 41 A6 A0 85 83 84 8E 86 8F 91 92 62 42 63 43 87 80 64 44 65 45 82 90 8A 88 89 66 46 9F 67 47 68 48 69 49 A1 8D 8C 8B 6A 4A 6B 4B 6C 4C 6D 4D 6E FC 4E A4 A5 6F 4F A7 A2 95 93 94 99 70 50 71 51 72 52 73 53 E1 74 54 75 55 A3 97 96 81 9A 76 56 77 57 78 58 79 59 98 7A 5A E0 E2 EB EE E9 E3 E5 E4 E7 ED E8 EA
复制代码

TOP

本帖最后由 plp626 于 2012-3-5 10:42 编辑

回复 30# plp626

:: char2asc 版本2(二分法)每次调用相当于执行20次左右set赋值

    接30楼:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. :: 初始化: 定义单字符if顺序表@ifasc; 全局变量 @ifcode
  4. call ascmap.cmd /437 @
  5. set @ifcode=20FF090A0B0C0D2101020304050607080E0F101112131415161718191A1B1C1D1E1F7F272D222324252628292A2C2E2F3A3B3F405B5C5D5E5F607B7C7D7EADA82BF93C3D3EF1AEAFF6FBEFF7F0F3F2A9F4F5FEC4CDB3BADAD5D6C9BFB8B7BBC0D4D3C8D9BEBDBCC3C6C7CCB4B5B6B9C2D1D2CBC1CFD0CAC5D8D7CEDFDCDDDEDBB0B1B29B9C9DAAF8E6FA9E30ACAB3132FD33343536373839EC6141A6A08583848E868F91926242634387806444654582908A888966469F674768486949A18D8C8B6A4A6B4B6C4C6D4D6EFC4EA4A56F4FA7A2959394997050715172527353E174547555A39796819A7656775778587959987A5AE0E2EBEEE9E3E5E4E7EDE8EA
  6. for /l %%a in (0 2 510)do for %%b in ("!@ifcode:~%%a,2!")do set @ifasc=!@ifasc!!@:~0x%%~b,1!
  7. ::char2asc版本2(code by plp) 入口参数#1,出口参数##,需要全局变量@ifasc和@ifcode
  8. set #1=A
  9. set $=!@ifasc!
  10. set n=0&for %%a in (128 64 32 16 8 4 2 1)do if "!#1!" geq "!$:~%%a,1!" set $=!$:~%%a!&set/a n+=%%a*2
  11. set/a ##=0x!@ifcode:~%n%,2!
  12. set ##
  13. pause
复制代码
经比较,28楼char2asc(耗时比2100+),效率略胜二分法版本(耗时比2400+)
1

评分人数

    • CrLf: 应该是最快的办法了吧技术 + 1

TOP

回复 35# terse


显然,这说明预处理耗时随代码长度的增长不是线性增长的,看来代码简短是保证效率高的一个要求。。。

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

回复 37# terse


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

TOP

回复 41# terse

是的;

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

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

TOP

返回列表