标题:提高代码效率的常用技巧及方案1[20090607]
首发地址:http://www.bathome.net/thread-4831-1-2.html
首发日期:2009-06-07
更新日期:无
本帖适合对批处理有一定基础、对 for 流程比较了解的新手观看。
本文出自 批处理之家论坛 bbs.bathome.net 转载请注明。
批处理入门不久的新手们,当对批处理有一定的理解了,能写出些像样的代码来的时候,是否发现自己的代码比起一些老鸟们的却总是运行慢些呢?原因很简单,因为还有一些有关效率方面的技巧,是初级教科书和教程中所没有提及(或是根本没有)的。下面将为你们介绍些论坛会员通过讨论、测试、实践得到的一些经验。
以下讨论内容都是经过论坛众多会员讨论、研究得出的结论、技巧,
我在这里稍微作一下总结,便于新手更方便快捷的学习。
有不对的地方欢迎指出,若有新的(或不同的)观点可以跟帖讨论。
原讨论帖子链接汇总: http://www.bathome.net/thread-4828-1-1.html
以下所有总结只有果没有因,就是说不讨论原因,只讨论结果,
因为这涉及到cmd的机制问题,讨论起来将是长篇大论,(主要是我也不懂cmd的机制 ^_^)
我们只要知道是这么回事就可以了。比如 call 命令,我们不用知道它为什么慢,
只要知道它慢,而且怎样避开这个问题就可以了。
进入正题:
一、call 命令
call 命令我们用的应该是非常多的,它可以调用另一个文件,可以跳转到标签,可以延迟变量。
用的最多的应该是 跳转和延迟变量了,就从它们说起:
先说跳转到标签 call :loop
在编写bat代码时,若需反复使用某个代码段时,我们经常会用 call :loop 的方式,这样可以使代码简洁。
但是需注意以下几点:
1、当call :loop语句用在某循环语句内部时,我们应根据循环次数决定是否要采用此方式
比如:- for /l %%a in (1 1 10) do call :loop
- pause
- :loop
- set /a n+=1
- goto :EOF
复制代码 for /l 的循环次数为10次,不多,对效率的影响不明显,我们可以这样使用,但如果循环次数为100或1000呢?
那么效率是无法忍受的,这时我们应该尽量改变代码的设计,把:loop 处的代码移到for内部来,这样效率就会大大提升。
如:- for /l %%a in (1 1 100) do set /a n+=1
复制代码 上面只是简单的测试代码,当然实际中没人会用call来完成,但有时遇到复杂情况,
:loop段的代码不仅仅只是set /a n+=1这么简单时,就必须重新设计代码结构了,非万不得已,尽量别在循环内部使用call 标签。
2、call 延迟变量 call set num=%%var:~!n!,1%%
需在循环语句中使用变量嵌套时,很多新手没办法只好采用call set num=%%var:~!n!,1%% 的方式,
(事实上这也是最常见的用法之一,甚至很多老鸟都还在这样使用)
使得代码效率不理想,下面就介绍一种处理这种情况的办法。
先看一段常规代码- @echo off&setlocal enabledelayedexpansion
- set "var=abcdefghij"
- for /l %%a in (1 1 10000) do (
- set /a n=!random!%%10
- call set num=%%var:~!n!,1%%
- echo !num!
- )
- pause
复制代码 以上代码是随机显示变量var的一个字符,这里虽然开启了延迟变量,但也不得不用call来再次扩展,导致速度狂慢。
因为在复合语句中必须使用!号来引用变量,而变量嵌套又无法只使用一种符号来完成
比如 set num=!var:~!n!,1! 或 set num=%var:~!n!,1% 都是不对的。
若使用 set num=!var:~%n%,1! 语法是对,但因为变量n是在复合语句中得到的值,所以不能用%号来引用,
没办法只好 call set num=%var:~!n!,1%%
这个时候我们就可以通过技巧避开call的使用,使速度大为提升。
解决办法:用for来中转一下变量n的值,因为for的变量是不用 ! % 来引用的。- :技巧:
- @echo off&setlocal enabledelayedexpansion
- set var=abcdefghij
- for /l %%a in (1 1 10000) do (
- set /a n=!random!%%10
- for %%i in (!n!) do set num=!var:~%%i,1!
- echo !num!
- )
- pause
复制代码 二、echo. 显示空行
这个命令相信潜水时间够长的新手应该都很清楚,它就是避免出现“ECHO 处于关闭状态。”的情况。
比如以下代码- @echo off
- for /f "tokens=1* delims=:" %%a in ('findstr /n .* a.txt') do echo %%b
- pause
复制代码 上面代码当a.txt中有空行时,就会显示 ECHO 处于关闭状态。这显然不是我们想要的结果。
给echo后面加上一个点,就可以正确得到空行。
除加一个点以外,还可以加 ,;=/\ 都可以,值得注意的是这么多符号,只有加这个.点是最影响效率的
具体讨论见这个帖 http://bbs.bathome.net/thread-4482-1-10.html 这里我们只要知道别用点来完成任务就可以。
三、启用 IO 次数
IO 是什么我们不讨论,嘿嘿,其实我也不知道 (^_^)
但是在批处理的效率中和它有很大关系,我们暂且就当它是个专业名词吧。
常规重定向到文本,我们是这样用的- @echo off
- cd.>b.txt
- for /f "delims= " %%a in (a.txt) do echo %%a>>b.txt
复制代码 或者更复杂点- @echo off&setlocal enabledelayedexpansion
- cd.>b.txt
- for /f "delims=" %%a in (a.txt) do (
- set var=%%a
- if "!var!"=="a" (echo abcd>>b.txt) else echo %%a>>b.txt
- echo 1234>>b.txt
- )
复制代码 我对IO的理解,以上代码每运行一次 >重定向 命令,就开启一次IO,这对效率是非常大的影响。
我们可以这样来解决,不但使得代码简洁易读,还大大提升效率,何乐而不为呢。- ::技巧
- @echo off&setlocal enabledelayedexpansion
- (for /f "delims= " %%a in (a.txt) do (
- set var=%%a
- if "!var!"=="a" (echo abcd) else echo %%a
- echo 1234
- ))>b.txt
复制代码 看出区别了吗?所有echo后面的重定向都省略了,只要最后一个>b.txt 就可以,还可以省略开头的cd.>b.txt
这就是只开启一次 IO,遇大文本时,效率惊人。。
注意:方法是给整个for语句用一对()扩起来,不只是for语句任何语句都可以,也可以把整个代码都扩起来,
只是需要注意,这样扩起来后,里面所有语句都变成是在复合语句中的语句了,使用时注意变量延迟的问题。
四、反复循环文本的代码
这个问题无法给出实际解决方案,只能是提个建议,以后遇到问题时应该考虑到这个问题,具体的还得根据实际情况改变代码的设计。
例:- @echo off&setlocal enabledelayedexpansion
- :loop
- set /a n+=1
- for /f "tokens=%n%" %%a in (a.txt) do (
- set /a m+=1
- call set .!n!=%%.!n!%% %%a
- )
- if !n! lss 3 goto loop
- set .
- pause
复制代码 以上代码为了使文本内容纵列横向显示,采取了对a.txt多次循环的办法,若非不得以,绝不建议如此使用,因为若文本稍大点的话,无疑会浪费很多次循环,导致代码效率降低。写代码应尽可能的减少对文本的循环次数,最好是只循环一次。当然这就要你写代码的技巧了。
五、外部命令在循环中的使用
dir、find、findstr 等cmd的外部命令,
即:不是嵌入到cmd.exe内部的命令,dir不确定是不是,但它同样影响效率。
先看两个代码
代码1、- @echo off
- for /r c:\ %%a in (*) do (
- for /f "delims=" %%a in ("%%a") do echo %%a
- set /a n+=1
- )
- echo %n%
- pause
复制代码 代码2、- @echo off
- for /r c:\ %%a in (*) do (
- dir /b "%%a"
- set /a m+=1
- )
- echo %m%
- pause
复制代码 以上两个代码在我的电脑中运行11333个文件,
第一个耗时10 秒 67 毫秒
第二个耗时1 分钟 16 秒 98 毫秒
可以看出区别了吧,如果是使用 find 或 findstr 效率差别还要明显。
所以在编写代码时应尽量注意,不要在循环语句中使用这些外部命令,或根据循环次数决定是否使用。
六、for+命令
for /f "delims=" %%a in ('dir c:\') do .....
这是很常用的语法,因为for可以把某命令的结果当作文本来循环。也是 for 的强大之处。
但是我们在使用中同样要考虑效率问题,而在适当的时候再采取这种用法,由其在循环语句中,尽量避免使用。
先看我们常用到的对文本行的数字排序的代码
假设a.txt内容如下- 27850 3758 5739 22253 28262
- 15464 30413 30033 24543 24661
- 1853 8091 23919 17560 19680
- 23470 21890 23992 27313 32488
- 19728 19875 10358 26951 15913
复制代码 排序代码- @echo off
- for /f "delims=" %%a in (a.txt) do (
- setlocal enabledelayedexpansion
- for %%i in (%%a) do (
- set "str=0000000000%%i"
- set .!str:~-10!=%%i
- )
- for /f "tokens=2 delims==" %%i in ('set .') do set var=!var! %%i
- echo !var!
- endlocal
- )
- pause
复制代码 是不是感觉显示结果不太流畅?
原因就是因为在for循环中使用了for+set . 命令。for本身和set . 命令都不是耗时的命令,但在循环中反复运行则会使代码效率大为降低,具体讨论见这里http://bbs.bathome.net/thread-3591-1-10.html
这里我们只要知道这种情况会影响代码效率,从而在设计代码时尽量绕开就可以了。
先到这里,待续 2009-06-07
=====================传说中的分割线,强力分割更新前后的内容=====================
|