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

[文本处理] 求字符串长度,简短高效的批处理代码(多种算法)

这是迄今为至,我多年来看过的批处理代码中,见到的最优美的一段代码,不和大家分享一下实在不好意思。
原以为,求字符串长度的方法在二分法和查表法后不会有突破了,但我发现时我的思想是太懒了。

二分回溯法(现在更正下,准确的叫法应该是优化的二分法)求字符串长度:

原帖:http://www.dostips.com/forum/viewtopic.php?f=3&t=1429
  1. setlocal enabledelayedexpansion
  2. set "$=!%1!#"
  3. set len=&for %%a in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1)do if !$:~%%a^,1!. NEQ . set/a len+=%%a&set $=!$:~%%a!
  4. endlocal&If %2. neq . (set/a%2=%len%)else echo %len%
复制代码
关于求字符串长度,以前论坛里讨论过不少,但就代码长度,支持最长字符串,和执行耗时来综合考虑,
我“个人认为”都没有超越上面这段代码。


把原代码小修小补增加了30多K的字节(原帖是联合256位的表),可以减少相当于大概2~7次set 赋值命令所耗费的时间(效率提高8%~30%):
  1. :strlen <stringVarName> [retvar]
  2. :: 思路: 二分回溯联合查表法
  3. :: 说明: 所求字符串大小范围 0K ~ 8K;
  4. ::    stringVarName ---- 存放字符串的变量名
  5. ::    retvar      ---- 接收字符长度的变量名
  6. setlocal enabledelayedexpansion
  7. set "$=!%1!#"
  8. set N=&for %%a in (4096 2048 1024 512 256 128 64 32 16)do if !$:~%%a!. NEQ . set/aN+=%%a&set $=!$:~%%a!
  9. set $=!$!fedcba9876543210&set/aN+=0x!$:~16,1!
  10. endlocal&If %2. neq . (set/a%2=%N%)else echo %N%
复制代码
对于上面代码
如果所求字符串长度不超过4096:
可以将4096 2048 1024 512 256 128 64 32 16改为2048 1024 512 256 128 64 32 16

如果所求字符串长度不超过2048:
可以将4096 2048 1024 512 256 128 64 32 16改为      1024 512 256 128 64 32 16
以此类推。。。

=============================================================
测试一下效率
  1. @echo off&setlocal enabledelayedexpansion
  2. :: 将strlen函数内敛到变量_strlen中,测试字符串长度小于4096;入口参数#1,返回变量##
  3. set "_strlen=set $=^!#1^!#&set ##=&(for %%a in (2048 1024 512 256 128 64 32 16)do if ^!$:~%%a^!. NEQ . set/a##+=%%a&set $=^!$:~%%a^!)&set $=^!$^!fedcba9876543210&set/a##+=0x^!$:~16,1^!"
  4. :: 生产一个长度为4096字符个数的字符串
  5. set str=.&for /l %%a in (1 1 12)do set str=!str!!str!
  6. :: 测试一下速度,4000次
  7. set bt=%time%
  8. for /l %%a in (1 1 4000)do (
  9.         set #1=!str:~-%%a!
  10.         (%_strlen%)
  11.         echo 长度:!##!
  12. )
  13. set et=%time%
  14. :: 计算花费时间
  15. set /a ct=1!et:~-5,2!!et:~-2!-1!bt:~-5,2!!bt:~-2!,ct+=-6000*("ct>>31")
  16. echo ---------------------&echo 开始时间:%bt%&echo 结束时间:%et%
  17. echo 4000次测试用时:%ct% 跑秒&echo -----------------------
  18. pause
复制代码
7

评分人数

太专业了 厉害啊

TOP

本帖最后由 SQYSQYSQY 于 2019-1-1 11:57 编辑
  1. :ac
  2. set ac=!%~1!!%~1!!%~1!^
  3. 3FF3FE3FD3FC3FB3FA3F93F83F73F63F53F43F33F23F13F03EF3EE3ED3EC3EB3EA3E93E83E73E63E53E43E33E23E13E03DF3DE3DD3DC3DB3DA3D93D83D73D63D53D43D33D23D13D03CF3CE3CD3CC3CB3CA3C93C83C73C63C53C43C33C23C13C03BF3BE3BD3BC3BB3BA3B93B83B73B63B53B43B33B23B13B03AF3AE3AD3AC3AB3AA3A93A83A73A63A53A43A33A23A13A039F39E39D39C39B39A39939839739639539439339239139038F38E38D38C38B38A38938838738638538438338238138037F37E37D37C37B37A37937837737637537437337237137036F36E36D36C36B36A36936836736636536436336236136035F35E35D35C35B35A35935835735635535435335235135034F34E34D34C34B34A34934834734634534434334234134033F33E33D33C33B33A33933833733633533433333233133032F32E32D32C32B32A32932832732632532432332232132031F31E31D31C31B31A31931831731631531431331231131030F30E30D30C30B30A3093083073063053043033023013002FF2FE2FD2FC2FB2FA2F92F82F72F62F52F42F32F22F12F02EF2EE2ED2EC2EB2EA2E92E82E72E62E52E42E32E22E12E02DF2DE2DD2DC2DB2DA2D92D82D72D62D52D42D32D22D12D02CF2CE2CD2CC2CB2CA2C92C82C72C62C52C42C32C22C12C02BF2BE2BD2BC2BB2BA2B92B82B72B62B52B42B32B22B12B02AF2AE2AD2AC^
  4. 2AB2AA2A92A82A72A62A52A42A32A22A12A029F29E29D29C29B29A29929829729629529429329229129028F28E28D28C28B28A28928828728628528428328228128027F27E27D27C27B27A27927827727627527427327227127026F26E26D26C26B26A26926826726626526426326226126025F25E25D25C25B25A25925825725625525425325225125024F24E24D24C24B24A24924824724624524424324224124023F23E23D23C23B23A23923823723623523423323223123022F22E22D22C22B22A22922822722622522422322222122021F21E21D21C21B21A21921821721621521421321221121020F20E20D20C20B20A2092082072062052042032022012001FF1FE1FD1FC1FB1FA1F91F81F71F61F51F41F31F21F11F01EF1EE1ED1EC1EB1EA1E91E81E71E61E51E41E31E21E11E01DF1DE1DD1DC1DB1DA1D91D81D71D61D51D41D31D21D11D01CF1CE1CD1CC1CB1CA1C91C81C71C61C51C41C31C21C11C01BF1BE1BD1BC1BB1BA1B91B81B71B61B51B41B31B21B11B01AF1AE1AD1AC1AB1AA1A91A81A71A61A51A41A31A21A11A019F19E19D19C19B19A19919819719619519419319219119018F18E18D18C18B18A18918818718618518418318218118017F17E17D17C17B17A17917817717617517417317217117016F16E16D16C16B16A16916816716616516416316216116015F15E15D15C15B15A159158^
  5. 15715615515415315215115014F14E14D14C14B14A14914814714614514414314214114013F13E13D13C13B13A13913813713613513413313213113012F12E12D12C12B12A12912812712612512412312212112011F11E11D11C11B11A11911811711611511411311211111010F10E10D10C10B10A1091081071061051041031021011000FF0FE0FD0FC0FB0FA0F90F80F70F60F50F40F30F20F10F00EF0EE0ED0EC0EB0EA0E90E80E70E60E50E40E30E20E10E00DF0DE0DD0DC0DB0DA0D90D80D70D60D50D40D30D20D10D00CF0CE0CD0CC0CB0CA0C90C80C70C60C50C40C30C20C10C00BF0BE0BD0BC0BB0BA0B90B80B70B60B50B40B30B20B10B00AF0AE0AD0AC0AB0AA0A90A80A70A60A50A40A30A20A10A009F09E09D09C09B09A09909809709609509409309209109008F08E08D08C08B08A08908808708608508408308208108007F07E07D07C07B07A07907807707607507407307207107006F06E06D06C06B06A06906806706606506406306206106005F05E05D05C05B05A05905805705605505405305205105004F04E04D04C04B04A04904804704604504404304204104003F03E03D03C03B03A03903803703603503403303203103002F02E02D02C02B02A02902802702602502402302202102001F01E01D01C01B01A01901801701601501401301201101000F00E00D00C00B00A009008007006005004^
  6. 003002001000&set /a "%2=0x!ac:~3069,3!"&goto :eof
复制代码
代码长点,但只要变量长度不超过1024,都可以用下面方法计算,保证高效

只要
call :ac 变量名 a
%a%就是长度
(注:变量名不带引号,不带百分号)


原理很简单,受楼主启发,将其变个型。
有人问这么一长串咋得到,用for命令即可。
@echo off
set "a="
for %%a in (3 2 1 0) do (
for %%b in (F E D C B A 9 8 7 6 5 4 3 2 1 0) do (
for %%c in (F E D C B A 9 8 7 6 5 4 3 2 1 0) do (
call set a=%%a%%%%a%%b%%c
)
)
)
echo %a%>1.txt

TOP

回复 29# CrLf

这两天偶然才翻到,感觉这句操作真的骚
  1. !%a:□=!!a:□=%!
复制代码
老了,思路跟不上了~

TOP

精典老贴! 再学习学习。

TOP

本帖最后由 CrLf 于 2018-8-5 21:02 编辑

回复 28# nwm310


    挺好的思路
    把变量拆分成7份分别查询,就可以查8191字符长度内的所有变量了

TOP

查表法
  1. ::0 ~ 255字
  2. setlocal enableDelayedExpansion
  3. set "s=your string"
  4. set a=□0□1□2□3□4□5□6□7□8□9□A□B□C□D□E□F
  5. set "$=!%a:□=!!a:□=%!!s!!s!"
  6. set /a len=0x!$:~-512,2!
  7. echo %len%
复制代码
  1. :: 0 ~ 1279字   (8191 /6 = 1365)
  2. setlocal enableDelayedExpansion
  3. set "s=your string"
  4. set a=□0□1□2□3□4□5□6□7□8□9□A□B□C□D□E□F
  5. set d=!%a:□=!!a:□=□%!
  6. set b=□0□1□2□3□4
  7. set "$=!%b:□=!!d:□=%!!s!!s!!s!"
  8. :: (0x4FF + 1 ) *3 = 1280 * 3 = 3840
  9. set /a len=0x!$:~-3840,3!
  10. echo %len%
复制代码
1

评分人数

TOP

回复 26# Batcher
  1. @echo off
  2. set "a=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  3. call :Ten a n
  4. echo 长度为%n%
  5. pause&exit
  6. :Ten
  7. setlocal enabledelayedexpansion
  8. set "$=!%1!#"
  9. set "len="&for %%a in (4096 2048 1024 256 128 64 32 16 8 4 2 1) do (if "!$:~%%a,1!" NEQ "" set/a len+=%%a&set "$=!$:~%%a!" &if "!$:~1!"=="" goto ok)
  10. set "s=!s!fedcba9876543210" &set/a "len+=0x!s:~16,1!"
  11. :ok
  12. endlocal&set/a "%2=%len%"&goto:eof
  13. pause
复制代码
增加 if "!$:~1!"=="" goto ok,可以在刚好够2分时,及时跳出循环。

TOP

回复 25# 踏沙行


让计算次数增加了6次

你有更加优化的代码吗?发出来大家一起讨论一下吧
如果字符串中存在有.(点号),也会影响正常判断吧

把你的测试用例和测试结果发出来看看
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

本帖最后由 踏沙行 于 2018-8-1 15:40 编辑

回复 24# Batcher
谢谢,是我写的有问题。
修正后再测试,代码如下:
  1. @echo off
  2. set "a=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
  3. rem a的长度为64
  4. call :Ten a n
  5. echo 长度为%n%
  6. pause&exit
  7. :Ten
  8. setlocal enabledelayedexpansion
  9. set "$=!%1!#"
  10. set "len="
  11. for %%a in (64 32 16 8 4 2 1) do (
  12. if !$:~%%a^,1!. NEQ . set/a len+=%%a&set $=!$:~%%a!
  13. set/a "T=T+1"&echo.第!T!次判断
  14. )
  15. endlocal&If %2. neq . (set/a%2=%len%)else echo %len%
复制代码
运行结果为:
第1次判断
第2次判断
第3次判断
第4次判断
第5次判断
第6次判断
第7次判断
长度为64
请按任意键继续. . .

【问题】本来字符长度为64,可以一次判断出结果。但是因为使用了set "$=!%1!#",让计算次数增加了6次
而且,对于if !$:~%%a^,1!. NEQ .这样的判断,如果字符串中存在有.(点号),也会影响正常判断吧

TOP

回复 23# 踏沙行


楼主代码没问题,是你的理解不太准确。

set a="1234567890abcdef"
这种写法,set命令会认为变量a的值是包含双引号的,18位是正确的

set "a=1234567890abcdef"
这种写法,set命令会认为变量a的值是不包含双引号的,16位是正确的
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

回复 22# Batcher
  1. @echo off
  2. set a="1234567890abcdef"
  3. call :Len a n
  4. echo 长度为%n%
  5. pause&exit
  6. :Len
  7. setlocal enabledelayedexpansion
  8. set "$=!%1!#"
  9. set len=&for %%a in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1)do if !$:~%%a^,1!. NEQ . set/a len+=%%a&set $=!$:~%%a!
  10. endlocal&If %2. neq . (set/a%2=%len%)else echo %len%
复制代码
执行结果:长度为18
实际上应该是16

TOP

回复 20# 踏沙行


    能否举例说明一下原代码哪个结果不正确?
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

回复 20# 踏沙行

set "$=!%1!#"
这不是没有意义的
提供bat代写,为你省时省力省事,支付宝扫码头像支付
微信: unique2random

TOP

回复 1# plp626

楼主的算法确实精简高效,把数学计算用到了极致。但这两天在学习时发现,该代码存在以下问题:
1、使用for /l (128 64 32...)时,判断某数为空后折半,需要把括号中的数减一后再判断,就如
if "!a:~127,1!" neq ""  set /a "len+=%%a",而不是if "!a:~%%a,1!" neq ""  set /a "len+=%%a"。用这种算法,固然可以在全部计算后最终加1得到正确长度,但是毕竟原代码结果不正确。

2、第9句的set/a "len+=0x!s:~16,1!",应该是set/a "len+=0x!s:~15,1!"

说的不对的地方,请斧正。

我的代码如下:
  1. @echo off
  2. set "var=1234567890abcdefghijklmnopqrstuv12345678901234567890abcdefghijklmnopqrstuv12345678901234567890abcdefghijklmnopqrstuv12345678901"
  3. call :Len var,n
  4. echo 长度为%n%
  5. pause&exit
  6. :Len
  7. Setlocal enabledelayedexpansion
  8. set "s=!%1!"
  9. for %%a in (512 256 128 64 32 16) do (
  10. set/a "m1=%%a-1" &call set "b1=%%s:~!m1!,1%%"
  11. if "!b1!" neq "" (
  12. set/a "len+=%%a" &set "s=!s:~%%a!"
  13. if "!s!"=="" goto ok )
  14. )
  15. set "s=!s!fedcba9876543210" &set/a "len+=0x!s:~15,1!"
  16. :ok
  17. endlocal&set/a "%2=%len%"&goto:eof
  18. pause
复制代码

TOP

返回列表