用VBS写的VBSCRIPT代码格式化工具VbsBeautifier
昨天在VBS吧看到一个精华帖《VBS代码格式化工具》,是用C++写的,区区VBS代码格式化,就不要劳C++大驾了吧,用VBS实现VBS代码格式化工具不是更自然么?
网上的VBS代码大部分都没有缩进,新手不知道要缩进,高手缩进了被某些个垃圾网站采集以后也就没有了缩进,还有以一些博客贴吧也会把缩进给吃掉。除了缩进之外,由于学VBS的大部分都是学批处理出身,代码风格还是跟写批处理一样难看。其实一般情况下用VbsEdit5.2.4.0自带的代码格式化功能就行了,没有必要重复造轮子。只不过VbsEdit5.2.4.0在格式化带有冒号的代码时不是很理想,加上我已经很久没有写过像样的VBS脚本了,所以还是决定造一下轮子。
2011年12月27日更新:在线VBScript代码格式化工具VbsBeautifier
因为代码比较长,所以贴在文章的最后,下面是VBS代码格式化工具的效果演示:
格式化前的VBS代码:
ONERRORRESUMENEXT:Setfso=CreateObject("Scripting.FileSystemObject"):X=0:T=true:WhiLeT Input=Inputbox("FilenameLowercaseBatchConvertor"&vbcrlf&vbcrlf&_ "Pleaseinputthedestinationfoldername.e.g.C:\Webmaster"&vbcrlf&vbcrlf&_ "Note:DoNOTadd'\'intheendoffoldername!","FLowercaseConvertor","C:\") iFInput=""then:Msgbox"Foldernameisempty!",48,"Error!":T=true:elseT=false:endIf:wend Msgbox"Allfilesnamesof"&Input&"willbeconvertedtolowercasenow...",64,"Note" fold(Input):Msgbox"Done!Total"&X&"file(s)wereconvertedtolowercase.",64,"Done" subfold(Path):SETf=fso.GetFolder(Path):Setrf=fso.GetFolder(Path).files:Setfc=f.SubFolders foREAChfffinrf:lcf1=LCase(fso.GetAbsolutePathName(fff)) fso.MoveFilefff,lcf1:X=X+1:next:forEacHf1infc:fold(f1) Setfile=fso.GetFolder(f1).files:fOREAChffiNfile:lcf=LCase(fso.GetAbsolutePathName(ff)) fso.MoveFileff,lcf:NEXT:NEXT:ENDsub
格式化后的VBS代码:
OnErrorResumeNext Setfso=CreateObject("Scripting.FileSystemObject") X=0 T=True WhileT Input=InputBox("FilenameLowercaseBatchConvertor"&vbCrLf&vbCrLf&_ "Pleaseinputthedestinationfoldername.e.g.C:\Webmaster"&vbCrLf&vbCrLf&_ "Note:DoNOTadd'\'intheendoffoldername!","FLowercaseConvertor","C:\") IfInput=""Then MsgBox"Foldernameisempty!",48,"Error!" T=True ElseT=False EndIf WEnd MsgBox"Allfilesnamesof"&Input&"willbeconvertedtolowercasenow...",64,"Note" fold(Input) MsgBox"Done!Total"&X&"file(s)wereconvertedtolowercase.",64,"Done" Subfold(Path) Setf=fso.GetFolder(Path) Setrf=fso.GetFolder(Path).files Setfc=f.SubFolders ForEachfffInrf lcf1=LCase(fso.GetAbsolutePathName(fff)) fso.MoveFilefff,lcf1 X=X+1 Next ForEachf1Infc fold(f1) Setfile=fso.GetFolder(f1).files ForEachffInfile lcf=LCase(fso.GetAbsolutePathName(ff)) fso.MoveFileff,lcf Next Next EndSub
VBS代码格式化工具的源码:
OptionExplicit IfWScript.Arguments.Count=0Then MsgBox"请将要格式化的代码文件拖动到这个文件上",vbInformation,"使用方法" WScript.Quit EndIf '作者:Demon '时间:2011/12/24 '链接:http://demon.tw/my-work/vbs-beautifier.html '描述:VBScript代码格式化工具 '注意: '1.错误的VBScript代码不能被正确地格式化 '2.代码中不能含有%[comment]%%[quoted]%等模板标签,有待改进 '3.由2可知,该工具不能格式化自身 DimBeautifier,i SetBeautifier=NewVbsBeautifier ForEachiInWScript.Arguments Beautifier.BeautifyFilei Next MsgBox"代码格式化完成",vbInformation,"提示" ClassVbsBeautifier 'VbsBeautifier类 Privatequoted,comments,code,indents PrivateReservedWord,BuiltInFunction,BuiltInConstants,VersionInfo '公共方法 '格式化字符串 PublicFunctionBeautify(ByValinput) code=input code=Replace(code,vbCrLf,vbLf) CallGetQuoted() CallGetComments() CallGetErrorHandling() CallColonToNewLine() CallFixSpaces() CallReplaceReservedWord() CallInsertIndent() CallFixIndent() CallPutErrorHandling() CallPutComments() CallPutQuoted() code=Replace(code,vbLf,vbCrLf) code=VersionInfo&code Beautify=code EndFunction '公共方法 '格式化文件 PublicFunctionBeautifyFile(ByValpath) Dimfso Setfso=CreateObject("scripting.filesystemobject") BeautifyFile=Beautify(fso.OpenTextFile(path).ReadAll) '备份文件以免出错 fso.GetFile(path).Copypath&".bak",True fso.OpenTextFile(path,2,True).Write(BeautifyFile) EndFunction PrivateSubClass_Initialize() '保留字 ReservedWord="AndAsBooleanByRefByteByValCallCaseClassConstCurrencyDebugDimDoDoubleEachElseElseIfEmptyEndEndIfEnumEqvEventExitExplicitFalseForFunctionGetGotoIfImpImplementsInIntegerIsLetLikeLongLoopLSetMeModNewNextNotNothingNullOnOptionOptionalOrParamArrayPreservePrivatePropertyPublicRaiseEventReDimRemResumeRSetSelectSetSharedSingleStaticStopSubThenToTrueTypeTypeOfUntilVariantWEndWhileWithXor" '内置函数 BuiltInFunction="AbsArrayAscAtnCBoolCByteCCurCDateCDblCIntCLngCSngCStrChrCosCreateObjectDateDateAddDateDiffDatePartDateSerialDateValueDayEscapeEvalExpFilterFixFormatCurrencyFormatDateTimeFormatNumberFormatPercentGetLocaleGetObjectGetRefHexHourInStrInStrRevInputBoxIntIsArrayIsDateIsEmptyIsNullIsNumericIsObjectJoinLBoundLCaseLTrimLeftLenLoadPictureLogMidMinuteMonthMonthNameMsgBoxNowOctRandomizeRGBRTrimReplaceRightRndRoundScriptEngineScriptEngineBuildVersionScriptEngineMajorVersionScriptEngineMinorVersionSecondSetLocaleSgnSinSpaceSplitSqrStrCompStrReverseStringTanTimeTimeSerialTimeValueTimerTrimTypeNameUBoundUCaseUnescapeVarTypeWeekdayWeekdayNameYear" '内置常量 BuiltInConstants="vbBlackvbRedvbGreenvbYellowvbBluevbMagentavbCyanvbWhitevbBinaryComparevbTextComparevbSundayvbMondayvbTuesdayvbWednesdayvbThursdayvbFridayvbSaturdayvbUseSystemDayOfWeekvbFirstJan1vbFirstFourDaysvbFirstFullWeekvbGeneralDatevbLongDatevbShortDatevbLongTimevbShortTimevbObjectErrorvbOKOnlyvbOKCancelvbAbortRetryIgnorevbYesNoCancelvbYesNovbRetryCancelvbCriticalvbQuestionvbExclamationvbInformationvbDefaultButton1vbDefaultButton2vbDefaultButton3vbDefaultButton4vbApplicationModalvbSystemModalvbOKvbCancelvbAbortvbRetryvbIgnorevbYesvbNovbCrvbCrLfvbFormFeedvbLfvbNewLinevbNullCharvbNullStringvbTabvbVerticalTabvbUseDefaultvbTruevbFalsevbEmptyvbNullvbIntegervbLongvbSinglevbDoublevbCurrencyvbDatevbStringvbObjectvbErrorvbBooleanvbVariantvbDataObjectvbDecimalvbBytevbArrayWScript" '版本信息 VersionInfo=Chr(39)&Chr(86)&Chr(98)&Chr(115)&Chr(66)&Chr(101)&Chr(97)&Chr(117)&Chr(116)&Chr(105)&Chr(102)&Chr(105)&Chr(101)&Chr(114)&Chr(32)&Chr(49)&Chr(46)&Chr(48)&Chr(32)&Chr(98)&Chr(121)&Chr(32)&Chr(68)&Chr(101)&Chr(109)&Chr(111)&Chr(110)&Chr(13)&Chr(10)&Chr(39)&Chr(104)&Chr(116)&Chr(116)&Chr(112)&Chr(58)&Chr(47)&Chr(47)&Chr(100)&Chr(101)&Chr(109)&Chr(111)&Chr(110)&Chr(46)&Chr(116)&Chr(119)&Chr(13)&Chr(10) '缩进大小 Setindents=CreateObject("scripting.dictionary") indents("if")=1 indents("sub")=1 indents("function")=1 indents("property")=1 indents("for")=1 indents("while")=1 indents("do")=1 indents("for")=1 indents("select")=1 indents("with")=1 indents("class")=1 indents("end")=-1 indents("next")=-1 indents("loop")=-1 indents("wend")=-1 EndSub PrivateSubClass_Terminate() '什么也不做 EndSub '将字符串替换成%[quoted]% PrivateSubGetQuoted() Dimre Setre=NewRegExp re.Global=True re.Pattern=""".*?""" Setquoted=re.Execute(code) code=re.Replace(code,"%[quoted]%") EndSub '将%[quoted]%替换回字符串 PrivateSubPutQuoted() Dimi ForEachiInquoted code=Replace(code,"%[quoted]%",i,1,1) Next EndSub '将注释替换成%[comment]% PrivateSubGetComments() Dimre Setre=NewRegExp re.Global=True re.Pattern="'.*" Setcomments=re.Execute(code) code=re.Replace(code,"%[comment]%") EndSub '将%[comment]%替换回注释 PrivateSubPutComments() Dimi ForEachiIncomments code=Replace(code,"%[comment]%",i,1,1) Next EndSub '将冒号替换成换行 PrivateSubColonToNewLine code=Replace(code,":",vbLf) EndSub '将错误处理语句替换成模板标签 PrivateSubGetErrorHandling() Dimre Setre=NewRegExp re.Global=True re.IgnoreCase=True re.Pattern="on\s+error\s+resume\s+next" code=re.Replace(code,"%[resumenext]%") re.Pattern="on\s+error\s+goto\s+0" code=re.Replace(code,"%[gotozero]%") EndSub '将模板标签替换回错误处理语句 PrivateSubPutErrorHandling() code=Replace(code,"%[resumenext]%","OnErrorResumeNext") code=Replace(code,"%[gotozero]%","OnErrorGoTo0") EndSub '格式化空格 PrivateSubFixSpaces() Dimre Setre=NewRegExp re.Global=True re.IgnoreCase=True re.MultiLine=True '去掉每行前后的空格 re.Pattern="^[\t]*(.*?)[\t]*$" code=re.Replace(code,"$1") '在操作符前后添加空格 re.Pattern="[\t]*(=|<|>|-|\+|&|\*|/|\^|\\)[\t]*" code=re.Replace(code,"$1") '去掉<>中间的空格 re.Pattern="[\t]*<\s*>[\t]*" code=re.Replace(code,"<>") '去掉<=中间的空格 re.Pattern="[\t]*<\s*=[\t]*" code=re.Replace(code,"<=") '去掉>=中间的空格 re.Pattern="[\t]*>\s*=[\t]*" code=re.Replace(code,">=") '在行尾的_前面加上空格 re.Pattern="[\t]*_[\t]*$" code=re.Replace(code,"_") '去掉DoWhile中间多余的空格 re.Pattern="[\t]*Do\s*While[\t]*" code=re.Replace(code,"DoWhile") '去掉DoUntil中间多余的空格 re.Pattern="[\t]*Do\s*Until[\t]*" code=re.Replace(code,"DoUntil") '去掉EndSub中间多余的空格 re.Pattern="[\t]*End\s*Sub[\t]*" code=re.Replace(code,"EndSub") '去掉EndFunction中间多余的空格 re.Pattern="[\t]*End\s*Function[\t]*" code=re.Replace(code,"EndFunction") '去掉EndIf中间多余的空格 re.Pattern="[\t]*End\s*If[\t]*" code=re.Replace(code,"EndIf") '去掉EndWith中间多余的空格 re.Pattern="[\t]*End\s*With[\t]*" code=re.Replace(code,"EndWith") '去掉EndSelect中间多余的空格 re.Pattern="[\t]*End\s*Select[\t]*" code=re.Replace(code,"EndSelect") '去掉SelectCase中间多余的空格 re.Pattern="[\t]*Select\s*Case[\t]*" code=re.Replace(code,"SelectCase") EndSub '将保留字内置函数内置常量替换成首字母大写 PrivateSubReplaceReservedWord() Dimre,words,word Setre=NewRegExp re.Global=True re.IgnoreCase=True re.MultiLine=True words=Split(ReservedWord,"") ForEachwordInwords re.Pattern="(\b)"&word&"(\b)" code=re.Replace(code,"$1"&word&"$2") Next words=Split(BuiltInFunction,"") ForEachwordInwords re.Pattern="(\b)"&word&"(\b)" code=re.Replace(code,"$1"&word&"$2") Next words=Split(BuiltInConstants,"") ForEachwordInwords re.Pattern="(\b)"&word&"(\b)" code=re.Replace(code,"$1"&word&"$2") Next EndSub '插入缩进 PrivateSubInsertIndent() Dimlines,line,i,n,t,delta lines=Split(code,vbLf) n=UBound(lines) Fori=0Ton line=lines(i) SingleLineIfThenline t=delta delta=delta+CountDelta(line) Ift<=deltaThen lines(i)=String(t,vbTab)&lines(i) Else lines(i)=String(delta,vbTab)&lines(i) EndIf Next code=Join(lines,vbLf) EndSub '调整错误的缩进 PrivateSubFixIndent() Dimlines,i,n,re Setre=NewRegExp re.IgnoreCase=True lines=Split(code,vbLf) n=UBound(lines) Fori=0Ton re.Pattern="^\t*else" Ifre.Test(lines(i))Then lines(i)=Replace(lines(i),vbTab,"",1,1) EndIf Next code=Join(lines,vbLf) EndSub '计算缩进大小 PrivateFunctionCountDelta(ByRefline) Dimi,re,delta Setre=NewRegExp re.Global=True re.IgnoreCase=True ForEachiInindents.Keys re.Pattern="^\s*\b"&i&"\b" Ifre.Test(line)Then '方便调试 'WScript.Echoline line=re.Replace(line,"") delta=delta+indents(i) EndIf Next CountDelta=delta EndFunction '处理单行的IfThen PrivateSubSingleLineIfThen(ByRefline) Dimre Setre=NewRegExp re.IgnoreCase=True re.Pattern="if.*?then.+" line=re.Replace(line,"") '去掉PrivatePublic前缀 re.Pattern="(private|public).+?(sub|function|property)" line=re.Replace(line,"$2") EndSub EndClass 'Demon,于2011年平安夜
来源:http://demon.tw/my-work/vbs-beautifier.html