程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 一個用於格式化/統計文本的可擴展框架

一個用於格式化/統計文本的可擴展框架

編輯:關於VC++

說明

本文為TextFormator Framework的說明文檔,原名《TextFormator Framework Introduce》,您同樣可以在如下網址處找到有關TextFormator的詳細信息,包括源碼以及本文:

引言

萌發編寫TextFormator的最初原因和以下兩件事情有關:

1、年初的時候曾在csdn的論壇上提過一個問題,內容是詢問哪裡有好的“代碼統計分析工具”,當時並沒有得到什麼回應。

2、前段時間,一個同事向我展示了他在業余時間編寫的一個小工具,其功能是根據一段c++程序生成一個經過著色的html文本。

此後,我一直都在嘗試尋找一種通用的解決方案,以盡可能滿足包括上面提到的情況在內的多種應用要求。最近幸好有一段相對空閒的時間,從而讓我得以將這個想法付諸實現。我用一周的時間,完成了從設計到編碼到添加文檔注釋的全部工作,當然還包括撰寫此文^^,這讓我重新找回了大學時代的創作激情。記得還是大三時,自己曾經花了3天時間用匯編語言編寫了一個可以在文本狀態下支持分級菜單操作的“學生檔案管理程序”。也許只是一時興趣的驅使,也許這個簡單的框架並不一定具有相當的實用價值,也許確實存在制作精良的代碼統計分析工具。但是,編寫這個框架,對於我而言,卻是一次運用OO設計思想,實踐STL的不錯體驗。並且,將之作為Open Source發布的目的,也是在於“拋磚引玉”,希望對之有興趣的同仁,可以在此基礎上繼續發展和改進,這比起現成的工具而言有了更大的靈活性和針對性。

對於框架,如果您有什麼好的建議、想法或者問題,敬請與我聯系:[email protected]

功能簡介

這是一個Open Source的可擴展的應用程序框架,你可以通過對框架代碼的擴展以滿足不同應用需要。

TextFormator適用於對多種程序設計語言的源代碼進行處理(如:C/C++,Java,Pascal,Masm),同時也支持純文本處理,可以對之進行如下處理:

1、任意形式的格式化輸出,例如:生成經過著色的html文本,縮格排版,刪除注釋行。

2、不同形式的代碼統計分析,例如:關鍵字查找,注釋行統計,函數統計。

主要特點

代碼以Open source方式發布,並采用OO設計思想,力求具有相當的靈活性和可擴展性。

基於C++ STL,力求具有相當的可移植性。

總體處理流程

原始文本通過字符流的方式讀入內存,按行進行解析。解析後的結果字符流,將作為格式化處理/統計處理的輸入流。經過一次或多次格式化之後的字符流將被輸出到文件或標准輸出設備,而經過統計之後的字符流不做輸出,相應輸出的是某些統計結果。圖示如下:

圖一

類圖

目前而言,這個框架還不甚復雜。盡管如此,在設計時還是下了一些功夫的,這一點可以從稍後的“詳細介紹”部分看到。以下是框架的類圖總結。從中可以看到,框架的總體結構采用了一種類似Strategy Pattern的組織方式:

圖二

詳細介紹

core 部分

ParseHandler

抽象類,所有解析過程中所使用的handler都應派生自該類,它定義了此類handler(s)的基本行為。主要是一個accept方法,功能是將傳入其中的文本當前行,從當前位置開始,按指定的方式解析。而具體采用何種方式,則是subclass的事情。若解析成功,則將結果置於一個TokenInfo結構體中,然後指向當前行的下一段未解析字串,並返回true;否則返回false,以告知框架將控制權移交給下一個handler。至於如何編寫實際的handler,可以參考extend部分的以下諸類:StringParseHandler,NumberParseHandler,OperatorParseHandler,IdentifierParseHandler,WhitespaceParseHandler,CommentParseHandler

另外,ParseHandler中還引入了優先級的概念,具體實現有點類似java中的線程優先級。這麼做的理由是依據實際情況而來的:如果分析的是一段代碼,以c++/java為例,對於解析注釋的handler和解析標識符的handler,前者的優先級顯然大過後者,因為當標識符位於注釋中時,仍應將之當作注釋處理;另外對於解析字符串的handler而言,其優先級又是和解析注釋的handler相同的,因為當注釋位於字符串內時,仍應將之當作字符串處理,而當字符串位於注釋內時,則應當作注釋處理。在實際應用時,一般的handler只要使用NORM_PRIORITY即可,而對於處理類似注釋和字符串這樣的handler則要使用MAX_PRIORITY。此外,框架還預定義了一個特殊的DefaultParseHandler,其作用是當所有其它handler都無法解析當前字串時,就由它來頂替,其優先級被定義為MIN_PRIORITY。在派生你自己的handler時一般不要使用MIN_PRIORITY優先級。DefaultParseHandler的調用機會一般很少,並且其對字串的處理方式也是極其“粗魯”的,它的調用往往意味著你所擁有的解析現有文本的handler(s)還不夠齊全。

LineParser

對文本逐行逐字解析,解析完成後可以通過getTokensInfoList方法獲取解析結果。你可以通過registParseHandler方法和unreigistParseHandler方法在run-time階段動態的設置各種具體的parse handler,以適時的調整LineParser的行為。比如,你可以將一個原本解析c++程序的LineParser定制成解析匯編程序的LineParser。而實際上,前後是同一個Parser對象。

LineParser在被創建的時候會自動產生一個DefaultParseHandler,解析過程中會按優先級從高到低的順序調用不同的handler對當前字串進行解析,直至找到一個能處理當前字串的handler,如果所有handler都無法解析當前字串,則交由DefaultParseHandler全權處理。因為每次handler解析完之後,都會修改當前位置“指針”以指向本行的下一段未解析字串,而當該handler無法處理當前字串時是不會修改位置“指針”的,所以DefaultParseHandler的定義並非多余,它的出現是為了避免潛在的死循環,並且使解析過程能夠一直進行下去,以盡可能多的得到經過有效解析的信息,即使間或有DefaultParseHandler留下的“未經消化”的結果。

FormatHandler

抽象類,所有格式化輸出過程中所使用的handler都應派生自該類,它定義了此類handler(s)的基本行為。主要是一個format方法,功能是將傳入其中的解析後的字串,按特定方式處理。至於具體方式,則是subclass的事情。處理後的結果直接影響傳入其中的字串,所以,當多個handler對同一個字串進行處理時,要注意處理的先後順序,不同的順序可能得到不同的結果。

作為FormatHandler的一個附帶功能,通過和LineFormator的配合,你可以實現某些代碼/文本的分析統計功能。事實上,你只要在handler內部不對傳入其中的字串做任何修改動作,而只對其內容進行分析,就可以實現一些很有價值的功能了。比如:統計代碼中的注釋行,統計某些關鍵字的出現頻率,統計函數個數及長函數的出現頻率等等。統計的結果可以放在各自的handler內,extend部分的Count::KeywordCountHandler,Count::CommentCountHandler,以及Count.cpp演示了某些簡單的統計功能。

LineFormator

對解析後的文本逐行逐字進行格式化處理,處理完成後可以通過getFormattedLines方法獲取處理結果。你可以通過registFormatHandler方法和unreigistFormatHandler方法在run-time階段動態的設置各種具體的format handler,以適時的調整LineFormator的行為。比如,你可以將一個原本對c++程序進行縮格排版處理的LineFormator定制成產生Html輸出的LineFormator。而實際上,前後是同一個Formator對象。

與LineParser不同的一點是,LineFormator允許對同一類解析後的字串注冊多個format handler(s),亦即你可以對同一串字符進行多次處理,當然每次處理之後都會影響原有字串的面貌。比如,針對注釋,你可以為LineFormator注冊一個Indent::NormalFormatHandler,然後再注冊一個Htmlize::BodyFormatHandler,這樣的結果是,原有文本在經過縮格排版之後才產生html文本,你可以添加任意多的handler,任意組合這些handler,只要這種添加和組合有實際意義。這一特性,給實際應用帶來了便利和靈活性。要注意的一點是,框架調用handler(s)的順序是和你注冊它們的先後順序一致的。extend部分的Htmlize::BodyFormatHandler,Indent::NormalFormatHandler,Indent::WhitespaceFormatHandler,Indent::OperatorFormatHandler,以及Htmilze.cpp,Indent.cpp,IndentHtmilze.cpp演示了上面所述的內容。

helper 部分

Session

Session類的設計模仿了ASP/JSP中的Session功能,當然只是形似(主要是功能相似),還遠沒有那麼復雜。該類的主要作用是為了給各handler之間彼此傳遞信息提供方便,這包括解析過程中所使用的handler和格式化輸出過程中所使用的handler。其內部持有一個map容器,可以通過set方法向map中插入指定key所對應的value,再通過get方法獲取該value。采用這種設計是為了減輕框架代碼的負擔,框架無須具體負責handler之間的信息傳遞;同時也為日後的擴展帶來了方便,你可以在你自己的subclass handler中隨意使用Session以應對具體應用。

Context

代表解析過程和格式化輸出過程中的上下文環境。框架代碼會將諸如當前處理行號,當前處理字串(解析後),當前字串位置等信息傳入Context內,以備subclass handler(s)取用。未來的版本中可能會擴充Context所包含的信息。Context內部持有一個session成員。事實上,雖然前面曾經提到,各subclass handler可以通過Session對象彼此傳遞信息,但handler並不能直接訪問到Session,而是需要通過Context的session方法,才能得到實際的Session對象。其實,某種意義上來講,Session也是一種上下文。但是這種上下文的實際含義在框架代碼中是無法預知的,這一點和Context中的其他成員不同。也就是說,Context是一個半開放的對象,在框架力所能及的范圍之內,一些明確的穩定的信息不應放在Session裡面,它們大可以作為Context的固定成員,與Session平起平坐,因為隨著時間的推移,這些信息不會改變,所以就不會被當作hard code。至於其余框架不能確定的易變信息,則交由Session來處理比較合適。

FileHelper

一個文件操作的輔助類,提供了文件讀寫的功能,在框架的其他地方以及基於框架的具體應用中可以使用到它。

FileFinder

一個文件操作的輔助類,提供了遍歷指定目錄及其子目錄下特定文件的功能,在框架的其他地方以及基於框架的具體應用中可以使用到它。不過目前此類僅限於在Windows平台上使用,使用前需要定義宏__WINDOWS__,具體可見Portability.h文件。

extend 部分

目前,這裡包含了如下幾個部分:

從ParseHandler派生而來的若干解析處理類,包括:

解析字符串 StringParseHandler

解析數字 NumberParseHandler

解析運算符 OperatorParseHandler

解析標識符和關鍵字 IdentifierParseHandler

解析空格及制表符 WhitespaceParseHandler

解析注釋 CommentParseHandler

從FormatHandler派生而來的若干格式化處理類,包括:

格式化輸出Html文本 Htmlize::BodyOutputHandler

縮格排版 Indent::NormalOutputHandler,Indent::WhitespaceOutputHandler,Indent::OperatorOutputHandler

統計關鍵字和注釋行 Count::KeywordCountHandler,Count::CommentCountHandler

文件清單

說明:點擊鏈接可以查看對應的源碼文件,該文件是經過Htmlize處理之後的html文本

GeneralDefine.h 包含一些全局性的類型定義

Portability.h 包含一些在平台移植時用到的宏定義

LineParser.h 文本解析部件(Line Parser)的.h文件

LineParser.cpp 文本解析部件(Line Parser)的.cpp文件

LineFormator.h 文本格式化部件(Line Formator)的.h文件

LineFormator.cpp 文本格式化部件(Line Formator)的.cpp文件

ParseHandler.h 解析處理器(Parse Handler)的抽象類定義

DefaultParseHandler.h 一個預定義的缺省解析處理器

ConcreteParseHandlers.h 若干解析處理器派生類的定義及實現

FormatHandler.h 格式化處理器(Format Handler)的抽象類定義

HtmlFormatHandlers.h Format Handler的派生類的定義及實現,支持Html格式化輸出

IndentFormatHandlers.h Format Handler的派生類的定義及實現,支持縮格排版

CountFormatHandlers.h Format Handler的派生類的定義及實現,支持統計功能

Context.h 定義了解析及格式化輸出過程中的上下文背景

Session.h 用於在各Handler(s)之間傳遞信息的類

FileHelper.h 和文件操作相關的輔助類的.h文件

FileHelper.cpp 和文件操作相關的輔助類的.cpp文件

FileFinder.h 和文件操作相關的輔助類的.h文件

FileFinder.cpp 和文件操作相關的輔助類的.cpp文件

Htmlize.cpp 利用框架,將源代碼進行Html格式化輸出的演示程序

Indent.cpp 利用框架,對源代碼縮格排版的演示程序

IndentHtmlize.cpp 利用框架,對源代碼縮格排版後再進行Html格式化輸出的演示程序

Count.cpp 利用框架,對源代碼進行某些簡單統計的演示程序(統計void和for的出現次數,統計注釋行的出現次數)

Batch.cpp 演示FileFinder的使用方法,與其余演示程序配合使用,可以實現批量文件處理

問題、建議及其他

關於可移植性

我已先後在MSVC和g++上對框架代碼及示例程序做了測試,在MSVC中,還用了P.J. STL和STLport分別做了測試,並改正了一些錯誤,具體請見隨源碼所附的ChangeLog。其中,MSVC命令行編譯器的版本是12.00.8168 for 80x86,g++命令行編譯器的版本是egcs-2.91.57 19980901 (egcs-1.1 release),STLport的版本是4.5 release。目前為止,代碼在上述幾個平台下的測試是成功的。

效率和靈活性的矛盾

該框架在處理效率上並非最優,但與之相比,框架所具有的靈活性和可擴展性更具吸引力,因此在仔細權衡了這對矛盾之後,我選擇了後者。

目前框架中不甚滿意及可以改進的幾個地方

LineFormator在作為統計分析用的時候,某些接口和代碼是多余的,雖然這並不影響使用。

現有的框架在解析具體文本時,是通過定義ParseHandler的派生類來實現的。這樣做的副作用是為了支持新類型文本的解析,需要改動源代碼,以添加新的ParseHandler的派生類。另一種可行的方案是定義一個template文件,將與解析新類型文本相關的信息放入其內。這樣,每次無需改動源碼,而只需修改template文件即可。但是,這樣做就需要合理抽取各種程序設計語言的共同特點以形成template,對框架代碼的要求,也相應提高了。

對於框架擴展的一些建議

目前的框架並沒有GUI界面,所提供的演示例程也僅是在命令行狀態下運行,在將來的擴展中,希望可以加入GUI特性,但是作為擴展,這不應該屬於框架的范疇。

關於擴展該框架時namespace的命名建議:

對於框架之上parse handler的擴展,若適用於多種程序設計語言,則采用形如TextFormator::XXX的命名方式,XXX代表具體的parse handler(比如:TextFormator::StringParseHandler)。若僅適用於一種程序設計語言,則采用形如TextFormator::Language_Name::XXX的命名方式,Language_Name代表語言名稱(比如:TextFormator::Pascal::CommentParseHandler)

對於某些具體應用,若需求相對穩定,可以考慮加入TextFormator的extend部分,並采用形如TextFormator::Util_Name::XXX的命名方式,Util_Name代表應用名稱(比如:TextFormator::Htmlize::BodyFormatHandler)

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved