Board logo

标题: 【擂台赛】将字符串转换为ascii值的高效代码 -- 批处理完成 [打印本页]

作者: plp626    时间: 2012-3-1 23:42     标题: 【擂台赛】将字符串转换为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次以上,多次求平均值以减小误差(比值越小自然代码效率越高),最后再亮代码,这样很有意思(代码别太长)
作者: cjiabing    时间: 2012-3-2 00:04

批处理字符编码(密码加密)工具
http://www.bathome.net/thread-15659-1-1.html
虽然不同,但也差不多。
作者: powerbat    时间: 2012-3-2 00:10

为什么我会vbs,知道vbs就不想在bat上深挖了。。。
作者: plp626    时间: 2012-3-2 00:20

恩;
我想会有人在bat版问:vbs一行代码的事情为什么要多行去做?
答案在风中。。。
作者: powerbat    时间: 2012-3-2 00:27

ASCII字符集以外的字符用纯bat好像没办法吧。
作者: plp626    时间: 2012-3-2 00:31

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

能处理这些就OK了
作者: lllsoslll    时间: 2012-3-2 01:36

本帖最后由 lllsoslll 于 2012-3-2 02:36 编辑

我的 version 1.0
  1. set str=0123_ABCDXYZabcdxyz
  2. rem 版本一代码
复制代码
测试代码运行结果:
一次任务与一次“@echo off命令”耗时比=6219

继续优化。。
我的 version 1.1
  1. set str=0123_ABCDXYZabcdxyz
  2. rem 版本一代码
复制代码
测试代码运行结果:
一次任务与一次“@echo off命令”耗时比=5756
作者: CrLf    时间: 2012-3-2 05:16

版本二:
一次任务与一次“@echo off命令”耗时比=1571
一次任务与一次“@echo off命令”耗时比=1463
用了外部命令...
作者: CrLf    时间: 2012-3-2 05:36

个人感觉纯内部命令方案的循环量太大,无法用变量替换简化,而且受所需兼容的范围影响太严重,故而没打算往那个方向去考虑。

换了一个方案,只用一个外部命令,不过结果反而更慢了...
\w范围:
一次任务与一次“@echo off命令”耗时比=2944
可打印字符范围:
一次任务与一次“@echo off命令”耗时比=3094
作者: batman    时间: 2012-3-2 09:42

我不是来唱反调的,我就是想问一下研究是不是要和实用性挂上钩才是王道?
作者: garyng    时间: 2012-3-2 11:41

用 VBS+BAT?

作者: terse    时间: 2012-3-2 12:21

第一想到 VBS + BAT
纯P的效率不够撒
作者: powerbat    时间: 2012-3-2 12:35

目前我只想到逐字查表法,将=和!之外的字符定义成变量,=和!单独处理。
作者: lllsoslll    时间: 2012-3-2 13:24

看来我的代码效率好低,是8楼的1/3
作者: CrLf    时间: 2012-3-2 15:29

回复 13# powerbat


    嗯,这应该是类似情况下循环量较小的方案,当其他所有字符都被处理为数字后,! 可以用 %str:!=33% 直接替换,而等号可以利用预处理替换为空格再替换为 61,但是虽然避免了大量的循环,定义大量变量带来的效率损失也同样是很恐怖的...


回复 14# lllsoslll


    我的方案虽然看起来效率比较高,但没有算法可言...而且按楼主给出的格式计算 echo off 与代码的效率比值在不同电脑上似乎结果不同(有趣的是,方案一是2900+,方案二的值是1400+,把之前另一台机子的结果颠倒了)
作者: lllsoslll    时间: 2012-3-2 16:58

本帖最后由 lllsoslll 于 2012-3-2 17:06 编辑

回复 15# CrLf



恩;
只是大略消除;而且比值的思想是假定电脑配置和环境的变化,使得echo off耗时和代码耗时成正比例变化的;事实上应该不是线性关系;
姑且用近似线性关系来消除偏差;毕竟二者都是随配置提升耗时减少的,单调性一致;


因为求比值的时候,echo off是100000次;代码任务是500次;
最后给代码任务的耗时乘以200;
比值的结果后两位数都是不精确的位数;
作者: lllsoslll    时间: 2012-3-2 16:59

回复 15# CrLf


    想知道如何利用预处理把等号替换为空格
作者: CrLf    时间: 2012-3-2 17:42

本帖最后由 CrLf 于 2012-3-2 17:49 编辑

回复 17# lllsoslll


    前不久写过将空格替换为几乎任意字符的高兼容性方案:
  1. @echo off
  2. set str=bbs.bathome.net=abc=123=ABC=@#$=test========
  3. rem 设置源字符串
  4. set 要替换的字符=[#]
  5. set "空="
  6. set "k= "
  7. set "t= "   ;此处引号内的内容为 t=tab制表符
  8. rem 初始化设置
  9. setlocal enabledelayedexpansion
  10. set str=!str:"=""!
  11. rem 将引号倍增,迫使所有字符均在引号对之外
  12. for %%a in (^^ ^& ^| ^( ^) ^< ^> ";" ",") do set "str=!str:%%~a=^^^%%~a!"
  13. rem 替换特殊字符
  14. endlocal&set "str=%str:!=^^!%"
  15. rem 关闭变量延迟,同时保留 str 变量
  16. set "str=%str: =!t!%"   ;此处引号内的内容为 str:tab制表符=!t!
  17. rem 分别替换制表符为变量 !t!
  18. for /f "delims=" %%a in (""%str: =!k!%"") do set str=%%~a
  19. rem 核心语句,替换空格为变量 !k! 后,利用等号在 for 中是分隔符的性质使其被预处理为空格
  20. setlocal enabledelayedexpansion
  21. set "str=!str: =%要替换的字符%!"
  22. rem 完成替换
  23. set "str=!空!%str%"
  24. rem 完成 !k!=空格、!t!=tab制表符 以及 ^ 的消隐,!空! 变量是用于在 %str% 不含 ! 号时触发二次转义
  25. set str=!str:""="!
  26. rem 引号减半,还原现场
  27. echo !str!
  28. rem 输出结果
  29. pause
复制代码
但是因为在 powerbat 的方案中,除了 = 与 ! 之外所有字符都被处理成 asc 了,所以可以这样:
  1. @echo off&setlocal enalbedelayedexpansion
  2. rem 运行环境
  3. rem [[[[[此行代表 powerbat 的方案实现代码]]]]]
  4. endlocal&set str=%str:!=33 %
  5. rem 关闭变量延迟,替换感叹号
  6. set str=%str: =space%
  7. rem 替换空格为 space
  8. for /f "delims=" %%a in (""%str%"") do set "str=%%~a"
  9. rem 替换等号为空格
  10. set str=%str: =32 %
  11. rem 替换空格(原来是等号)为 32
  12. set str=%str:space= %
  13. rem 替换 space 为空格
  14. echo %str%
  15. pause
复制代码

作者: lllsoslll    时间: 2012-3-2 18:38

回复 18# CrLf

一直以来,
    for /f "delims=" %%a in (""..........."")do ......
还有
   for /f "usebackq" ...
在xp中被认为是cmd for /f 的bug(提示找不到文件。。乱码。。);

我这里xp,你那个代码如果把.net与abc之间的等号,换成两个等号,即:
bbs.bathome.net==abc=123====ABC=@#$=test========
这个bug就犯了;

是不是win7修复了这个bug;

另外,我的这里连续的多个等号被当做一个等号替换了;
作者: CrLf    时间: 2012-3-2 18:41

本帖最后由 CrLf 于 2012-3-2 18:47 编辑

回复 19# lllsoslll


    测试了一下,xp 下也能替换,但是我之前没有注意到连续的多个等号确实被替换为单个空格了,嗯,看来还是只能老老实实逐字判断
作者: lllsoslll    时间: 2012-3-2 18:43

回复 18# CrLf


    还有一点,当usebackq不犯的时候,
for /f "delims=" %%a in (""string"")do ............
预处理string时,不仅把=视作空格,像参数分割符(= 、,、;、tab)都被视作空格;

(xp系统)
作者: CrLf    时间: 2012-3-2 18:47

回复 21# lllsoslll


    所以才要先对其他特殊字符进行转义处理~
作者: lllsoslll    时间: 2012-3-2 18:49

回复 20# CrLf


    不错,很好;我一直对usebackq这个bug视作怪物,usebackq的特性我一直就废弃了;
以后用这个^"string^"的转义代替这个特性了;
作者: lllsoslll    时间: 2012-3-2 20:06

回复 18# CrLf


    变量名和替换字符不分大小写,变量法或直接替换获取的话,怕是大问题
作者: CrLf    时间: 2012-3-3 02:34

回复 24# lllsoslll


    先把能替换的替换了,不方便替换的再用偏移处理也许会很快呢?反正没有宽字符干扰,判断是否字母用 if xx geq a ... 就行了

    呃,试了下纯内部命令的方案,结果跟我预想的不太一样,给出的比值貌似很不正常...
作者: plp626    时间: 2012-3-3 03:07

本帖最后由 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的所有字符;
明天贴代码。。。
作者: CrLf    时间: 2012-3-3 03:15

回复 26# plp626


    对长变量畏之如虎...不过这确实是一种极大节省循环量的方案,等代码
作者: plp626    时间: 2012-3-3 08:29

本帖最后由 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
复制代码

作者: plp626    时间: 2012-3-3 08:55

本帖最后由 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
复制代码

作者: plp626    时间: 2012-3-3 09:33

本帖最后由 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
复制代码

作者: dnfreeuser    时间: 2012-3-3 16:47

将ascii输出到文件,然后调用debug来显示行不?
作者: CrLf    时间: 2012-3-3 17:57

回复 29# plp626


    貌似是我电脑上的内部命令运行太慢...@echo off 效率为 20312.8HZ
"find/?"与"@echo off" 耗时比=1657
"find/?"与"set a=#4647" 耗时比=1281

另:判断是否大/小写时我更喜欢用 delims
  1. set chr=a
  2. for /f "delims=ABCDEFGHIJKLMNOPQRSTUVWXYZ" %%a in ("%chr%") do echo 小写
复制代码

作者: CrLf    时间: 2012-3-3 18:07

我的三个方案都很老实,没有什么出彩的地方(而且还内置了你的函数),但还是贴出来吧...

com 方案:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set str= ^^!"$%%&'()*+,-./09:;<=>?@AZ[^_`az|}~
  4. set<nul>$ /p=@!str!
  5. debug asc.com<%0 >nul 2>nul
  6. asc<$>ans.txt
  7. for /f "delims=" %%a in (ans.txt) do set ans=%%a
  8. echo %ans%
  9. pause&exit
  10. e100 B4 08 CD 21 B4 00 BB 64 00 E8 23 00 B3 0A 89 C8
  11. e110 E8 1C 00 88 CA 80 C2 30 CD 21 B2 20 CD 21 B4 0B
  12. e120 CD 21 3C 00 75 DA B4 09 BA 43 01 CD 21 CD 20 F6
  13. e130 F3 88 E1 38 F8 74 0B B7 FF B4 02 88 C2 80 C2 30
  14. e140 CD 21 C3 0D 0A 24
  15. rcx
  16. 46
  17. w
  18. q
复制代码
fc 方案
  1. @echo off
  2. set str=0123_ABCDXYZabcdxyz
  3. setlocal enabledelayedexpansion
  4. set<nul>$ /p"=@!str!"
  5. fc /b $ %comspec%>#
  6. for /f "eol=F skip=2 tokens=2" %%a in (#) do set "ans=!ans!%%a "
  7. echo %ans%
  8. pause
复制代码
纯内部命令方案(0x20~0x7f 范围):
  1. @echo off&setlocal enabledelayedexpansion
  2. set str= ^^!"$%%&'()*+,-./09:;<=>?@AZ[^_`az|}~
  3. set $=!str!#
  4. set N=&for %%z in (4096 2048 1024 512 256 128 64 32 16)do if !$:~%%z!. NEQ . set/aN+=%%z&set $=!$:~%%z!
  5. set $=!$!fedcba9876543210&set/aN+=0x!$:~16,1!-1
  6. set $= ^^!"#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^^_`abcdefghijklmnopqrstuvwxyz{|}~
  7. set "$=                                !$!"
  8. (for /l %%a in (32 1 127) do (
  9.    for /f "delims=" %%b in (":!$:~%%a,1!") do if "!str%%b=!" neq "!str!" echo %%a !$:~%%a,1!
  10. ))>#
  11. set $=
  12. for /l %%a in (0 1 %n%) do (
  13.    for /f "tokens=1,2 delims= " %%b in (#) do (
  14.       if "!str:~%%a,1!"=="%%c" set "ans=!ans!%%b "
  15.    )
  16. )
  17. echo %ans%
  18. pause
复制代码
纯内部命令方案([a-z_A-Z] 范围),结果测试时反而没有前一个方案快:
  1. @echo off
  2. set str=0123_ABCDXYZabcdxyz
  3. setlocal enabledelayedexpansion
  4. set $=!str!
  5. set N=&for %%z in (4096 2048 1024 512 256 128 64 32 16)do if !$:~%%z!. NEQ . set/aN+=%%z&set $=!$:~%%z!
  6. set $=!$!fedcba9876543210&set/aN+=0x!$:~16,1!
  7. set $=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
  8. (for %%z in ("48 1 57" "65 1 90" "96 1 122") do (
  9.    for /l %%a in (%%~z) do (
  10.       for /f "delims=" %%b in ("!$:~,1!") do (
  11.          if "!str%%b=!" neq "!str!" echo %%a !$:~,1!
  12.          set $=!$:~1!
  13.       )
  14.    )
  15. ))>#
  16. set $=
  17. for /l %%a in (0 1 !n!) do (
  18.    for /f "tokens=1,2 delims= " %%b in (#) do (
  19.       if "!str:~%%a,1!"=="%%c" set "$=!$!%%b "
  20.    )
  21. )
  22. set ans=!$:96=95!
  23. echo %ans%
  24. pause
复制代码

作者: plp626    时间: 2012-3-4 12:33

本帖最后由 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+)
作者: terse    时间: 2012-3-4 17:50

为什么 测试值这么高啊 比用FC搞个临时文件也高好多
  1. :::::::::::::::::::::::::::::::: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
  2. @echo off
  3. set /p var=<%0
  4. setlocal EnableDelayedExpansion
  5. for /l %%i in (32 1 126) do (
  6.    if defined _"!var:~%%i,1!" (
  7.       set "#!var:~%%i,1!=!var:~%%i,1!"
  8.       set "$#!var:~%%i,1!=%%i"
  9.    ) else set _"!var:~%%i,1!"=!var:~%%i,1!&set "$!var:~%%i,1!=%%i"
  10. )
  11. set str= ^^!"$%%&'()*+,-./09:;<=>?@AZ[^^_`az|}~
  12. set "Slen=!str!"
  13. for %%i in (4096 2048 1024 512 256 128 64 32 16)do if "!Slen:~%%i!" NEQ "" set/aLen+=%%i&set Slen=!Slen:~%%i!
  14. set Slen=!Slen!FEDCBA9876543210&set/aLen+=0x!Slen:~16,1!
  15. ECHO !LEN!
  16. for /l %%i in (0 1 !Len!) do (
  17.     for /f "delims=" %%j in ("!str:~%%i,1!") do (
  18.         if "%%j" == "" (set ANS=!ANS! 33)else (
  19.            if "!#%%j!" equ "" (if "!$%%j!" equ "" (set ANS=!ANS! 61)else set ANS=!ANS! !$%%j!) else (
  20.               if "!#%%j!" lss "%%j" (set ANS=!ANS! !$%%j!) else set ANS=!ANS! !$#%%j!
  21.            )
  22.         )
  23.     )
  24. )
  25. echo!ANS!
  26. pause
复制代码

作者: plp626    时间: 2012-3-5 11:05

回复 35# terse


显然,这说明预处理耗时随代码长度的增长不是线性增长的,看来代码简短是保证效率高的一个要求。。。
作者: terse    时间: 2012-3-5 18:40

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

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

另:CrLf版主的 FC 方案 省去临时文件?
作者: plp626    时间: 2012-3-5 20:45

本帖最后由 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
复制代码

作者: plp626    时间: 2012-3-5 22:34

回复 37# terse


    terse 能否发下你的700+的测试源代码?
作者: terse    时间: 2012-3-5 22:43

  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 ....
复制代码

作者: terse    时间: 2012-3-5 22:58

回复 39# plp626
测试代码取自一楼的 是如上面如此嵌入吗
作者: plp626    时间: 2012-3-5 23:10

回复 41# terse

是的;

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

看来我低估了定义变量索引的思路。。
作者: CrLf    时间: 2012-3-6 03:34

本帖最后由 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 系统下估计会体现得更明显
作者: terse    时间: 2012-3-6 17:52

回复 43# CrLf
我的意思是 假如临时文件$大于 CMD 应该出错的哦  当然此案另外
作者: CrLf    时间: 2012-3-6 18:31

回复 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
复制代码

作者: terse    时间: 2012-3-6 18:51

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

回复 45# CrLf
还是不以为这样的流程 是取文件的16进制
我以为还应用等大空文件 不是吗?(当然这里不讨论这个话题了)
作者: neorobin    时间: 2014-10-17 16:29

本帖最后由 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
复制代码

作者: terse    时间: 2014-10-17 19:19

回复 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.     )
复制代码

作者: CrLf    时间: 2014-10-17 20:36

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

回复 47# neorobin


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

作者: neorobin    时间: 2014-10-17 21:08

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

回复 49# CrLf

不是 d

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

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





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