用正則表達式解析C#文件(updated)
2024-07-21 02:18:52
供稿:網友
想必很多讀者都寫過給程序代碼按語法著色的程序。而這在一段時間以前是一件很困難的事。你需要寫大量代碼分析語法——而這往往又是最困難的部分。直到,正則表達式(regular expression)的出現,我們才可以從繁重的工作中解脫。正則表達式提供了一系列方法(標準、模式),使我們能夠高效地創建、比較和修改字符串,以及迅速地分析大量文本和數據以搜索、移除和替換文本模式 [1] 。dotnet framework 提供了 system.text.regularexpression 命名空間來實現他們承諾的功能。
1. 正則表達式 [2]
首先,我想先簡單介紹一下正則表達式。
正則表達式最早是由數學家stephen kleene于1956年提出,他是在對自然語言的遞增研究成果的基礎上提出來的。具有完整語法的正則表達式使用在字符的格式匹配方面上,后來被應用到熔融信息技術領域。自從那時起,正則表達式經過幾個時期的發展,現在的標準已經被iso(國際標準組織)批準和被open group組織認定。
正則表達式并非一門專用語言,但它可用于在一個文件或字符里查找和替代文本的一種標準。它具有兩種標準:基本正則表達式(bre),擴展正則表達式(ere)。ere包括bre功能和另外其它的概念。
先進已有xsh,egrep,sed,vi以及在unix平臺下的程序實現了正則表達式。它們可以被很多語言采納,如html 和xml,這些采納通常只是整個標準的一個子集。隨著正則表達式移植到交叉平臺的程序語言的發展,它的功能也日益完整,使用也逐漸廣泛。
2. 相關的表達式
有關正則表達式我只能說這么多了——它是一個不小的知識體系,不可能用只言片語就解釋清楚。這里我只介紹與c#語法分析相關的結個匹配串。詳細內容請參見本blog站的收藏 regular expression specification [ the open group ] 。 另外,如果你已經對正則表達式有了相當的了解,那你可以略過下面每一條的解釋,以盡快完成全文。
i> 字符串 "(//?.)*?"
正則表達式中除 . $ ^ { [ ( | ) * + ? / 外,其他字符與自身匹配。在上面的式子中,兩邊的quotation mark就是指匹配字符串兩邊的引號。“//”表示一個“/”字符。后面緊跟的“?”表示匹配零個或一個字符。“.” 與除 /n 之外的任何字符匹配。
“()”表示捕獲匹配的子字符串。使用 () 的捕獲根據左括號的順序從1 開始自動編號。捕獲元素編號為零的第一個捕獲是由整個正則表達式模式匹配的文本。括號后面的“*”表示存在一個或多個這樣的子字符串。即“*”是作用于“(//?.)”的。
“?”的存在使空字符串也可以被捕獲。
ii> 逐字字符串 @"(""|.)*?"
匹配類似于 @"hello ""world ""!" 的字符串。
與用 | (垂直條)字符分隔的任何一個術語匹配;例如, cat|dog|tiger 。使用最左側的成功匹配。
iii> c# 文檔信息中的xml元素 ////s*<.*>
匹配c#自動化xml文檔。“/s”表示任何空白字符。需要注意的是,請不要隨意修改大小寫。因為在正則表達式是大小寫敏感的,在它的通配符中,大小寫字符往往表示完全相反的意思。比如,“/s”表示任何非空白字符。(下面的 “/z”也是這樣)
iv> c# 文檔信息中的內容 ////s?.*
v> 空行 ^/s*/z
“^”指定匹配必須出現在字符串的開頭或行的開頭。而“/z”表示指定匹配必須出現在字符串的結尾或字符串結尾的 /n 之前。
vi> c# 注釋 //.*
vii> c# 關鍵字 (abstract|where|while|yield){1}(/.|(/s)+|;|,|/(|/[){1}
篇幅所限,這兒只列出了很少幾個關鍵字(c#有至少80個關鍵字 ^_^)。需要注意的是,解析器會匹配左邊第一個成功項。因此,具有包含關系的單詞應注意順序:包含者要放在被包含者之前。例如:(in|int) 解析其會查不到 int,所以應該是 (int|in)。
除此之外還有,所有的括號 (/{|/[|/(|/}|/]|/)) 。
3. 相關類與其成員 [3]
[serializable]
public class regex : iserializable
// 表示不可變的正則表達式。
regex 類包含若干靜態方法,使您無需顯式創建 regex 對象即可使用正則表達式。使用靜態方法等效于構造 regex 對象,使用該對象一次然后將其銷毀。
regex 類是不可變(只讀)的,并且具有固有的線程安全性。可以在任何線程上創建 regex 對象,并在線程間共享。
以上摘自微軟的開發文檔。我們還需要用到它的幾個成員:
// 在指定的輸入字符串中搜索 regex 構造函數中指定的正則表達式匹配項。
public match match(
string intput
)
對于 match 類
[serializable]
public class match : group
// 表示單個正則表達式匹配的結果。有關 group 的詳細信息請參見微軟開發文檔。
我們會用到它的下列成員
// 原始字符串中發現捕獲的子字符串的從零開始的起始位置。
public int index { get; }
// 捕獲的子字符串的長度。
public int length { get; }
// 通過匹配捕獲的實際子字符串。
public int value { get; }
// 獲取一個值,該值指示匹配是否成功。
public bool success { get; }
// 獲取由正則表達式匹配的組的集合。
public virtual groupcollection groups { get; }
// 從上一個匹配結束的位置(即在上一個匹配字符之后的字符)開始
// 返回一個包含下一個匹配結果的新 match。
public match nextmatch();
以及 group 類的相應成員(上面列出的 match 的成員中,前四個屬性都是由 group 類繼承而來,因此這些成員將不再一一列出)。
匹配字符串必須在 regex 類的實例初始化的時候指定。你可以使用構造函數創建一個實例,使用它,然后銷毀它。或者直接使用靜態方法,這等效于創建實例。不過,經過測試,我發現靜態方法要稍稍慢于編譯的 regex 對象。請看下面的一組測試數據:
4. 撰寫代碼
我們現在需要對第三節中列出的c#語言元素進行分析。我所采取的是逐行分析(如果要采取多行分析,則相關表達式需要進行修改 [4] )。
using system.text.regularexpression;
// some other codes ... ...
// 首先創建 regex 實例(以字符串的解析為例)。
regex doublequotedstring = new regex( "/"(////?.)*?/"" );
// 然后去匹配字符串。
match m;
for( m = doublequotedstring.match( strsomecodes ) ; m.success ; m.nextmatch() ) {
foreach( group g in m.groups ) {
// do some drawings
}
}
剩下的事就是寫著色代碼了。
5. 源代碼
注:
[1] "能夠……文本模式" 引自 .net framework 常規參考 中的 正則表達式語言元素
[2] 正則表達式簡介 此處有關正則表達式的簡介參考自 zdnet china 技術與開發 中的相關內容。
[3] 本節中出現的類與函數的簽名與注釋均出自微軟文檔。
[4] 多行分析 詳情請參見 .net framework 常規參考 正則表達式語言元素