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

[原创] [BAT代码分析]如何判断字符串的长度

批处理不像其他的语言有专门的命令或函数来获取字符串的长度,要想判断字符串的长度,一切都要自力更生,通过自己编写代码予以解决。你可别小看这个简单的任务,真正要编写一段判断字符串长度的代码,其中还有不少值得注意的地方呢。

  以下是本人在前人工作的基础上编写的一段较为完美的演示代码,大家先拿来玩玩,玩过之后再看后面的分析。
  1. @echo off
  2. :Main
  3. cls
  4. set num=0
  5. set str=
  6. set /p str=请输入一串测试字符:
  7. if not defined str goto end
  8. set "str=%str:"=%"
  9. if defined str call :count
  10. :end
  11. echo 字符串去掉所有的双引号后,长度为:%num%
  12. pause
  13. goto Main
  14. :count
  15. set /a num+=1
  16. set "str=%str:~1%"
  17. if defined str goto count
  18. goto :eof
复制代码
以上这段代码的基本思路是:先去掉字符串中所有可能存在的双引号,然后,抛弃字符串首位的字符之后,再判断剩下的字符串是否为空;在抛弃首位字符的同时,计数器加1;若处理后的字符串不为空,则进入循环体:count,再重复执行抛弃首位字符、计数器加1、判断剩下的字符串是否为空这一过程,直到剩下的字符串是空值为止。

  思路是简单的,代码是短小的,但是,陷阱也是随处可遇的,一不留神,就会栽了跟斗。

  因为这段代码是可以重复演示的,所以,最开始需要对变量进行初始化:计数变量num赋初值为0,字符串变量str进行清空处理,否则,进行第二次演示的时候,上一次的结果将会带到第二次计算中来,导致最终结果出错。

  在这段代码中,if defiend str 这样的语句出现的频率很高,达到了3次:第1次是判断是否输入了字符串,第2次是在替换掉所有可能存在的双引号后,判断变量的值是否为空,第3次是在抛弃首位字符后,判断剩下的字符串中是否为空。

  (补充知识:if defiend str 语句是判断str的值是否为空的终极方法,使用 if "%str%"=="" 的句式是不完美的,具体分析请看这篇文章:判断变量值是否为空的终极方法 http://bbs.bathome.net/thread-4046-1-1.html

  也许有人说,if defined str 没必要保留那么多,只需要保留最后一个就可以了。

  果真如此吗?

  我们来分析一下是否有必要保留3个if defined str。

  当我们取消第1条 if defined str 语句后,如果输入的是空值,set "str=%str:"=%" 执行之后,str的值将为"=,而不是空值(为什么需要使用set "str=%str:"=%",请看后续分析),结果将出错,因此,第1条 if defined str 必须保留;
  如果我们取消第二条 if defined str ,直接使用call :count,那么,当 set "str=%str:"=%" 执行的结果令str的值为空的时候,第一次执行到:count标签段下的 set "str=%str:~1%" 时,因为str的初始值为空,则执行字符截取之后,str的结果将为~1,很显然,最终的结果将是错误的,因此,第2条 if defined str 语句也需要保留。
  最后一条 if defined str,是判断何时跳出循环体的语句,必须保留。

  对于最后的提示信息:字符串去掉所有的双引号后,长度为:%num%,很多人可能会大惑不解:为什么要去掉所有的双引号呢?难道不能统计双引号吗?

  对于双引号,确实是个令人头痛的问题,这得从批处理兼容特殊字符谈起。

  在批处理中,因为>、<、|、&等符号有特殊用法,若带入批处理作为一般字符处理,需要使用特殊的方法消除它们的原始用法,比如 set "str=%str:~1%" 这句,就是在 str=%str~1% 的首尾用引号把字符串括起来,消除那些特殊字符的特殊用法,当然,你也可以把 set "str=%str:~1%" 写为 set str="%str:~1%",同样能处理那些特殊字符,只不过,后面这一种会把双引号对本身作为 str 的值,在后续操作中还要对多出来的引号对做额外处理,而前一种写法则不会这样。但是,在这里又存在一个两难的选择:当其他特殊符号被正确处理之后,在特定情况下,双引号本身却不能被正确处理,比如偶数个引号后紧跟<、>、|等字符,这是由于引号对配对不正确引起的,因此,要么放弃对其他特殊字符的兼容而保留对引号的处理,要么先去掉所有的双引号而兼容更多的其他特殊字符。在本例中,采取的方案是后者,因此,在输入字符串之后,先用 set "str=%str:"=%" 去掉字符串中所有可能存在的双引号。

  set "str=%str:"=%" 一句,猛一看上去,两个str、三个双引号,完全没法分析语句结构,其含义是比较难以理解的。实际上,这是一条字符串替换语句,原型为:set str=%str1:str2=str3%,含义为:把 str1 中的字符串str2替换为str3,并把替换后的结果赋予str,一旦你把原型语句中的 str2 换为双引号,把 str3 换为空值,再在 set 语句的首尾加上兼容特殊字符的双引号之后,你就会明白 set "str=%str:"=%" 的含义为:把 str 这个变量中所有可能存在的双引号去掉,把结果再重新赋予str。具体分析过程可以参考这个帖子:[转帖]如何从用户传入的参数中去掉引号?  http://bbs.bathome.net/viewthread.php?tid=2397

  在本例中,set "str=%str:"=%"和set "str=%str:~1%"互为呼应,缺一不可。

  最后,我们来看一个小技巧: set /a num+=1。这是一条累加语句的缩写,原型为 set /a num=num+1,累加额度为每次在原有基础上加1,当然,你也可以加大累加额度,比如写成 set /a num+=2、set /a num+=3之类。与此类似的写法有:set /a num*=2表示累乘,set /a num/=2表示累除。缩写形式能减少代码量,也能增强可读性,是大家常用的书写格式。

  总结一下经验教训:

  1、随时初始化变量是个好习惯;
  2、随时检测变量的值是否为空,能消除不少隐患;
  3、兼容特殊字符,请使用引号对把字符串括起来,但是请首先保证要处理的字符串中没有双引号;


______________________________________________________

后记:

  本文发表之后,zqz0012005提供了一个更加完美的代码,经过初步测试,能兼容除空字符串、百分号外的其他所有特殊情形,具有更高的兼容性,也更简洁,现发在此处,等我空闲时间多了之后,再做一个对比分析,有意分析这段代码的,也不妨另开新帖加以讨论:
  1. @echo off
  2. set "str=Hello, bat! <^_^>%%""
  3. call :strlen len
  4. echo %len%
  5. pause&goto :eof
  6. :strlen
  7. setlocal EnableDelayedExpansion&set n=0
  8. :strlen_loop
  9. if "!str:~%n%,1!" neq "" set /a n+=1&goto strlen_loop
  10. endlocal&set "%~1=%n%"&goto :eof
复制代码
尺有所短寸有所长,学好批处理没商量;
考虑问题复杂化,解决问题简洁化。

心在天山,身老沧州。

一段较为完美的演示代码

这里是不是容易误导观众啊?namejm兄是否考虑在这里注明一下双引号的问题呢(虽然后文提到了)?
^_^
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

还有个判断字符串长度的习题呢,哪位有时间的话可以好好总结一下:
http://bbs.bathome.net/viewthread.php?tid=1480
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

熟知相关机制后其实也不麻烦。

计算字符串长度函数strlen
http://bbs.verybat.org/viewthread.php?tid=15407&page=1&fromuid=37#pid168106
命令行参考:hh.exe ntcmds.chm::/ntcmds.htm
求助者请拿出诚心,别人才愿意奉献热心!
把查看手册形成条件反射!

TOP

感觉这类代码不存在完美不完美,通用型代码往往效率不够高,针对型代码局限性太大。
还是应该具体情况具体分析,根据已知条件的多少来写针对性的代码是最适合的。
技术问题请到论坛发帖求助!

TOP

Re Bathcher 3 楼:
  既然声称较为完美,就不是绝对完美的意思,既然不加引号就已经表达了这层意思,我还是不用画蛇添足吧^_^。

Re zqz0012005 4 楼:
  看到了你推荐的那个链接,里面的思路能正确处理各种特殊字符,并且比我目前所写的代码更简洁,等空闲时间多了,我再为它写一个对比分析。

Re 随风 5 楼:
  通用性代码不一定效率就低,要看它在多大范围内通用,而通用和专用是相对而言的,往往可以互相转化,比如,从更高层面来看,在领域A里通用的代码,到了更高层级的B领域,它又成为了针对性代码,我们所追求的,不过是精益求精,在A领域实现通用之后,再追求更高层级B领域的通用而已。
尺有所短寸有所长,学好批处理没商量;
考虑问题复杂化,解决问题简洁化。

心在天山,身老沧州。

TOP

自个儿。。。慢慢看来,呵呵!!

TOP

分析的很深入,还需要进一步回味

TOP

受益匪浅。学习了~

TOP

回复 9# mikezunya


    goto 简单但是太慢了,还不如 for /l 固定循环八千次:
  1. set "length=0"
  2. for /l %%a in (0 1 8190) do if "!var:~%%a,1!" neq "" set "length=%%a"
复制代码
其实比较推荐用这个方案,简短,而且最高效:
http://bbs.bathome.net/viewthread.php?tid=11799

TOP

老师上课教了用二分法查找信息,如果我把它用来计算字符串长度,会不会效率更高呢?
经测试,在最坏情况下,也仅需循环十几次就可以得到结果
代码如下:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set /p str=input string:
  4. call :len ^!str!
  5. pause>nul
  6. exit
  7. :len
  8. set "t=%1"
  9. set long=0 & set longb=8192
  10. for %%a in (8192 4096 2048 1024 512 256 128 64 32 16 8 4 2) do (
  11. set /a zz=long+longb
  12. set /a "zz=zz/2"
  13. set /a zzs=zz+1
  14. set flag=false
  15. for %%b in (!zz!) do (if "!t:~%%b,1!" neq "" set flag=true)
  16. for %%b in (!zzs!) do (if !flag!==true if "!t:~%%b,1!"=="" echo !zzs! & goto:eof)
  17. if !flag!==false (set longb=!zz!) else (set long=!zz!)
  18. )
  19. if "!t!" neq "" (echo !zz!) else (echo 0)
  20. goto:eof
复制代码

TOP

返回列表