标题: [原创] [BAT代码分析]如何判断字符串的长度 [打印本页]
作者: namejm 时间: 2009-4-7 21:16 标题: [BAT代码分析]如何判断字符串的长度
批处理不像其他的语言有专门的命令或函数来获取字符串的长度,要想判断字符串的长度,一切都要自力更生,通过自己编写代码予以解决。你可别小看这个简单的任务,真正要编写一段判断字符串长度的代码,其中还有不少值得注意的地方呢。
以下是本人在前人工作的基础上编写的一段较为完美的演示代码,大家先拿来玩玩,玩过之后再看后面的分析。- @echo off
-
- :Main
- cls
- set num=0
- set str=
- set /p str=请输入一串测试字符:
- if not defined str goto end
- set "str=%str:"=%"
- if defined str call :count
-
- :end
- echo 字符串去掉所有的双引号后,长度为:%num%
- pause
- goto Main
-
- :count
- set /a num+=1
- set "str=%str:~1%"
- if defined str goto count
- 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提供了一个更加完美的代码,经过初步测试,能兼容除空字符串、百分号外的其他所有特殊情形,具有更高的兼容性,也更简洁,现发在此处,等我空闲时间多了之后,再做一个对比分析,有意分析这段代码的,也不妨另开新帖加以讨论:- @echo off
- set "str=Hello, bat! <^_^>%%""
- call :strlen len
- echo %len%
- pause&goto :eof
-
- :strlen
- setlocal EnableDelayedExpansion&set n=0
- :strlen_loop
- if "!str:~%n%,1!" neq "" set /a n+=1&goto strlen_loop
- endlocal&set "%~1=%n%"&goto :eof
复制代码
作者: Batcher 时间: 2009-4-7 21:27
这里是不是容易误导观众啊?namejm兄是否考虑在这里注明一下双引号的问题呢(虽然后文提到了)?
^_^
作者: Batcher 时间: 2009-4-7 21:36
还有个判断字符串长度的习题呢,哪位有时间的话可以好好总结一下:
http://bbs.bathome.net/viewthread.php?tid=1480
作者: zqz0012005 时间: 2009-4-7 21:41
熟知相关机制后其实也不麻烦。
计算字符串长度函数strlen
http://bbs.verybat.org/viewthread.php?tid=15407&page=1&fromuid=37#pid168106
作者: 随风 时间: 2009-4-7 21:56
感觉这类代码不存在完美不完美,通用型代码往往效率不够高,针对型代码局限性太大。
还是应该具体情况具体分析,根据已知条件的多少来写针对性的代码是最适合的。
作者: namejm 时间: 2009-4-7 22:37
Re Bathcher 3 楼:
既然声称较为完美,就不是绝对完美的意思,既然不加引号就已经表达了这层意思,我还是不用画蛇添足吧^_^。
Re zqz0012005 4 楼:
看到了你推荐的那个链接,里面的思路能正确处理各种特殊字符,并且比我目前所写的代码更简洁,等空闲时间多了,我再为它写一个对比分析。
Re 随风 5 楼:
通用性代码不一定效率就低,要看它在多大范围内通用,而通用和专用是相对而言的,往往可以互相转化,比如,从更高层面来看,在领域A里通用的代码,到了更高层级的B领域,它又成为了针对性代码,我们所追求的,不过是精益求精,在A领域实现通用之后,再追求更高层级B领域的通用而已。
作者: energy2009 时间: 2009-4-12 00:23
自个儿。。。慢慢看来,呵呵!!
作者: NeverOK 时间: 2009-5-5 23:04
分析的很深入,还需要进一步回味
作者: mikezunya 时间: 2013-8-8 16:31
受益匪浅。学习了~
作者: CrLf 时间: 2013-8-9 02:24
回复 9# mikezunya
goto 简单但是太慢了,还不如 for /l 固定循环八千次:- set "length=0"
- for /l %%a in (0 1 8190) do if "!var:~%%a,1!" neq "" set "length=%%a"
复制代码
其实比较推荐用这个方案,简短,而且最高效:
http://bbs.bathome.net/viewthread.php?tid=11799
作者: dengyuli 时间: 2015-1-9 15:53
老师上课教了用二分法查找信息,如果我把它用来计算字符串长度,会不会效率更高呢?
经测试,在最坏情况下,也仅需循环十几次就可以得到结果
代码如下:- @echo off
- setlocal enabledelayedexpansion
- set /p str=input string:
- call :len ^!str!
- pause>nul
- exit
- :len
- set "t=%1"
- set long=0 & set longb=8192
- for %%a in (8192 4096 2048 1024 512 256 128 64 32 16 8 4 2) do (
- set /a zz=long+longb
- set /a "zz=zz/2"
- set /a zzs=zz+1
- set flag=false
- for %%b in (!zz!) do (if "!t:~%%b,1!" neq "" set flag=true)
- for %%b in (!zzs!) do (if !flag!==true if "!t:~%%b,1!"=="" echo !zzs! & goto:eof)
- if !flag!==false (set longb=!zz!) else (set long=!zz!)
- )
- if "!t!" neq "" (echo !zz!) else (echo 0)
- goto:eof
复制代码
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |