介紹:
事實(shí)上我喜歡正則:他們做的很好了。 事實(shí)上他們太好了,以至于幾乎所有的程序員都必須使用它。
不幸的是每當(dāng)我需要一個(gè)正則的時(shí)候,我都會碰到同樣的問題:對于那些該死的語法我基本上都忘光了..
。如果我每天都要寫一個(gè)的話,我可能會很容易的記住他們,可是在一年里我基本上很少寫幾個(gè)..
在厭倦了一次又一次的查看和學(xué)習(xí)文檔之后,我決定通過String的擴(kuò)展方法來實(shí)現(xiàn)他們..
背景:
在處理大文本的驗(yàn)證,提取,編輯,替換或者是刪除一個(gè)給定模式的文本(例如一個(gè)郵箱地址)的時(shí)候,正則表達(dá)式非常強(qiáng)大和簡潔。
為了正確的使用正則表達(dá)式,你應(yīng)該:
一個(gè)用于分析的文本。
一個(gè)正則表達(dá)式解析引擎。
一個(gè)正則表達(dá)式(文本中用于查找分析的模式)
正則表達(dá)式的語法取決于你所使用的正則表達(dá)式解析引擎。 在微軟的世界里,這個(gè)正則表達(dá)式引擎類就是
System.Text.RegularExpressions.
它的語法在這里:http://msdn.microsoft.com/en-us/library/az24scfc.aspx
如果你想要一個(gè)關(guān)于正則表達(dá)式語法的介紹,請讀一下這篇寫的不錯的文章:
http://www.codeproject.com/Articles/9099/The-30-Minute-Regex-Tutorial
正則表達(dá)式的問題:
他們有他們的優(yōu)勢的缺點(diǎn):簡潔和強(qiáng)大的語法對與正則表達(dá)式引擎非常適合,可是并不適合人類閱讀。
當(dāng)你不熟悉他們的語法,你可以花上一整天的時(shí)間來寫一個(gè)正確的表達(dá)式,不過可能還要花更長的時(shí)間來驗(yàn)證它。讓正則表達(dá)式滿足你的期望是一件事,讓它只滿足你的期望則是另外一件事。
想法:
如果你熟悉SQL的話,你肯定知道 LIKE 操作符。為什么不把這個(gè)操作符帶到C#中呢?
為什么沒有一個(gè)可以處理大部分常見操作和能夠被正則表達(dá)式引擎執(zhí)行的簡潔語法呢?
一個(gè)簡化的語法
... 意味著更少的操作符。這里是我自己的一個(gè)列表:
? = 任何操作符
% = 0 或多個(gè)字符
* = 0 或多個(gè)字符,但是沒有空格(主要是單詞)
# = 任何單個(gè)數(shù)字 (0-9)
這是這些簡單語法的例子:
一個(gè)Guid可以被表示成 : ????????-????-????-????-????????????
一個(gè) email address 可能是 : *?@?*.?*
一個(gè)日期 : ##/##/####
正則表達(dá)式的狂熱分子已經(jīng)跳到桌子上了:很明顯最后一個(gè)正則表達(dá)式并不能確保匹配的是一個(gè)合適的日期,當(dāng)然他們是對的(那個(gè)表達(dá)式可以匹配99/99/9999). 在涉及驗(yàn)證的時(shí)候,它完全不能提供相同級別的功能。
頻繁的操作
你需要正則表達(dá)式引擎的頻繁的操作有哪些呢?
1:判斷一個(gè)文本是否匹配給定的模式:LIKE
2:根據(jù)一個(gè)給定的模式來查找文本:Search
3:提取文本中的字符串信息:Extract
做為使用正則表達(dá)式引擎的另一個(gè)選擇,這三個(gè)操作符'Like', 'Search' and 'Extract' 已經(jīng)通過String的擴(kuò)展方法實(shí)現(xiàn)了。
讓我們首先來談?wù)勊麄兊氖褂冒?..
1. 判斷是否一個(gè)字符串是否”Like” 給定的模式
如果你懂SQL的話,那么你就懂我說的...
如果一個(gè)字符串匹配指定的模式,Like操作符會簡單的返回true.
下面所有的例子都返回true,這意味著輸入的字符串都匹配他們的模式。
example:一個(gè)字符串是一個(gè)guid。
var result = "TA0E02391-A0DF-4772-B39A-C11F7D63C495".Like("????????-????-????-????????????");
example:guid結(jié)尾的字符串。
var result = "This is a guid TA0E02391-A0DF-4772-B39A-C11F7D63C495".Like(" %????????-????-????-????????????");
example:guid開頭的字符串。
var result = "TA0E02391-A0DF-4772-B39A-C11F7D63C495 is a guid".Like("????????-????-????-???????????? %");
example:包含guid的字符串
var result = "this string TA0E02391-A0DF-4772-B39A-C11F7D63C495 contains a guid".Like(" %????????-????-????-????????????%");
2. 在一個(gè)字符串中查找給定的模式
Search 擴(kuò)展方法可以在一個(gè)字符串中找到第一個(gè)匹配給定模式的子串。
example: 在字符串搜索guid
var result = "this string [TA0E02391-A0DF-4772-B39A-C11F7D63C495] contains a string matching".Search("[????????-????-????-????????????]") Console.WriteLine(result); // output: [TA0E02391-A0DF-4772-B39A-C11F7D63C495]
3. 通過給定的模式‘Extracting’ 字符串中的子串
基本和Like 搜索一樣,只是它返回的不是整個(gè)的字符串,而是一個(gè)匹配模式的子串?dāng)?shù)組。
example: 在一個(gè)字符串中返回guid的構(gòu)成部分
var result = "this string [TA0E02391-A0DF-4772-B39A-C11F7D63C495] contains a string matching".Extract("[????????-????-????-????????????]"); // result is an array containing each part of the pattern: {"TA0E02391", "A0DF", "4772", "B39A", "C11F7D63C495"}
example: 在一個(gè)字符串中返回email的構(gòu)成部分
var result = "this string contains an email: toto@domain.com".Extract("*?@?*.?*") // result is an array containing each part of the pattern: {"toto", "domain", "com"}
這里是代碼:
這個(gè)簡單的技巧就是:這3個(gè)公開的擴(kuò)展方法依賴于GetRegex 方法將這些簡潔的表達(dá)式轉(zhuǎn)換為一個(gè)有效的.net 正則表達(dá)式。
public static bool Like(this string item, string searchPattern) { return GetRegex("^" + searchPattern).IsMatch(item); } public static string Search(this string item, string searchPattern) { var match = GetRegex(searchPattern).Match(item); if (match.Success) { return item.Substring(match.Index, match.Length); } return null; } public static List<string> Extract(this string item, string searchPattern) { var result = item.Search(searchPattern); if (!string.IsNullOrWhiteSpace(result)) { var splitted = searchPattern.Split(new[] { '?', '%', '*', '#' }, StringSplitOptions.RemoveEmptyEntries); var temp = result; var final = new List<string>(); splitted.ForEach(x => { var pos = temp.IndexOf(x); if (pos > 0) { final.Add(temp.Substring(0, pos)); temp = temp.Substring(pos); } temp = temp.Substring(x.Length); }); if (temp.Length > 0) final.Add(temp); return final; } return null; } // private method which accepts the simplified pattern and transform it into a valid .net regex pattern: // it escapes standard regex syntax reserved characters // and transforms the simplified syntax into the native Regex one static Regex GetRegex(string searchPattern) { return new Regex(searchPattern .Replace("\\", "\\\\") .Replace(".", "\\.") .Replace("{", "\\{") .Replace("}", "\\}") .Replace("[", "\\[") .Replace("]", "\\]") .Replace("+", "\\+") .Replace("$", "\\$") .Replace(" ", "\\s") .Replace("#", "[0-9]") .Replace("?", ".") .Replace("*", "\\w*") .Replace("%", ".*") , RegexOptions.IgnoreCase); }
結(jié)論:
本文的意圖并不是代替Regex,而是提供一個(gè)簡單的方式來解決我需要使用正則表達(dá)式的80%的案例。
這個(gè)方式可以讓簡單的任務(wù)變得簡單,并且可以讓客戶端代碼更容易編寫和讓不熟悉的正則表達(dá)式語法的人容易理解。