我不喜歡正則...


 

介紹:

事實上我喜歡正則:他們做的很好了。 事實上他們太好了,以至於幾乎所有的程序員都必須使用它。

不幸的是每當我需要一個正則的時候,我都會碰到同樣的問題:對於那些該死的語法我基本上都忘光了..

。如果我每天都要寫一個的話,我可能會很容易的記住他們,可是在一年里我基本上很少寫幾個..

 

在厭倦了一次又一次的查看和學習文檔之后,我決定通過String的擴展方法來實現他們..

 

背景:

在處理大文本的驗證,提取,編輯,替換或者是刪除一個給定模式的文本(例如一個郵箱地址)的時候,正則表達式非常強大和簡潔。

 

為了正確的使用正則表達式,你應該:

  • 一個用於分析的文本。
  • 一個正則表達式解析引擎。
  • 一個正則表達式(文本中用於查找分析的模式)

正則表達式的語法取決於你所使用的正則表達式解析引擎。 在微軟的世界里,這個正則表達式引擎類就是

System.Text.RegularExpressions.

它的語法在這里:http://msdn.microsoft.com/en-us/library/az24scfc.aspx

如果你想要一個關於正則表達式語法的介紹,請讀一下這篇寫的不錯的文章:

 http://www.codeproject.com/Articles/9099/The-30-Minute-Regex-Tutorial

 

正則表達式的問題:

他們有他們的優勢的缺點:簡潔和強大的語法對與正則表達式引擎非常適合,可是並不適合人類閱讀。

當你不熟悉他們的語法,你可以花上一整天的時間來寫一個正確的表達式,不過可能還要花更長的時間來驗證它。讓正則表達式滿足你的期望是一件事,讓它只滿足你的期望則是另外一件事。

 

想法:

如果你熟悉SQL的話,你肯定知道 LIKE 操作符。為什么不把這個操作符帶到C#中呢?

為什么沒有一個可以處理大部分常見操作和能夠被正則表達式引擎執行的簡潔語法呢?

 

一個簡化的語法

... 意味着更少的操作符。這里是我自己的一個列表:

  • ? = 任何操作符 
  • % = 0 或多個字符
  • * = 0 或多個字符,但是沒有空格(主要是單詞)
  • # = 任何單個數字 (0-9)

這是這些簡單語法的例子: 

  • 一個Guid可以被表示成 : ????????-????-????-????-????????????
  • 一個 email address 可能是 : *?@?*.?*
  • 一個日期 : ##/##/####

 

正則表達式的狂熱分子已經跳到桌子上了:很明顯最后一個正則表達式並不能確保匹配的是一個合適的日期,當然他們是對的(那個表達式可以匹配99/99/9999).  在涉及驗證的時候,它完全不能提供相同級別的功能。

 

頻繁的操作

你需要正則表達式引擎的頻繁的操作有哪些呢?

  1. 1:判斷一個文本是否匹配給定的模式:LIKE
  2. 2:根據一個給定的模式來查找文本:Search
  3. 3:提取文本中的字符串信息:Extract

做為使用正則表達式引擎的另一個選擇,這三個操作符'Like', 'Search' and 'Extract' 已經通過String的擴展方法實現了。

 

讓我們首先來談談他們的使用吧...

1. 判斷是否一個字符串是否”Like” 給定的模式 

如果你懂SQL的話,那么你就懂我說的...

如果一個字符串匹配指定的模式,Like操作符會簡單的返回true.

下面所有的例子都返回true,這意味着輸入的字符串都匹配他們的模式。

 

example:一個字符串是一個guid。

var result = "TA0E02391-A0DF-4772-B39A-C11F7D63C495".Like("????????-????-????-????????????"); 

example:guid結尾的字符串。

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. 在一個字符串中查找給定的模式

Search 擴展方法可以在一個字符串中找到第一個匹配給定模式的子串。

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 搜索一樣,只是它返回的不是整個的字符串,而是一個匹配模式的子串數組。

example: 在一個字符串中返回guid的構成部分

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: 在一個字符串中返回email的構成部分

var result = "this string contains an email: toto@domain.com".Extract("*?@?*.?*")
    // result is an array containing each part of the pattern: {"toto", "domain", "com"}
 

這里是代碼:

這個簡單的技巧就是:這3個公開的擴展方法依賴於GetRegex 方法將這些簡潔的表達式轉換為一個有效的.net 正則表達式。

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);
}

 

結論:

本文的意圖並不是代替Regex,而是提供一個簡單的方式來解決我需要使用正則表達式的80%的案例。

這個方式可以讓簡單的任務變得簡單,並且可以讓客戶端代碼更容易編寫和讓不熟悉的正則表達式語法的人容易理解。

 

最后的結論:

1:本文是一篇翻譯文章,原文地址:I don't like Regex...。因為這篇文章的想法比較有意思,故此翻譯與大家分享。

2:這篇文章的評論也有點意思,大家可以看看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM