1. 程式人生 > >正則表示式並不難(轉)

正則表示式並不難(轉)

對於正則表示式,相信很多人都知道,但是很多人的第一感覺就是難學,因為看第一眼時,覺得完全沒有規律可尋,而且全是一堆各種各樣的特殊符號,完全不知所云。

其實只是對正則不瞭解而以,瞭解了你就會發現,原來就這樣啊正則所用的相關字元其實不多,也不難記,更不難懂,唯一難的就是組合起來之後,可讀性比較差,而且不容易理解,本文旨在讓大家對正則有一個基本的瞭解,能看得懂簡單的正則表示式,寫得出簡單的正則表示式,用以滿足日常開發中的需求即可。

0\d{2}-\d{8}|0\d{3}-\d{7} 先來一段正則,如果你對正則不瞭解,是不是完全不知道這一串字元是什麼意思?這不要緊文章會詳細解釋每個字元的含義的。

1.1 什麼是正則表示式

     正則表示式是一種特殊的字串模式,用於匹配一組字串,就好比用模具做產品,而正則就是這個模具,定義一種規則去匹配符合規則的字元。

1.2 常用的正則匹配工具

     線上匹配工具:

      http://tool.oschina.net/regex/

      用過幾個之後還是覺得這個是最好用的,支援將正則導成對應的語言如java C# js等還幫你轉義了,Copy直接用就行了很方便,另外支援把正則表示式用法解釋,如哪一段是捕獲分組,哪段是貪婪匹配等等,總之用起來 So Happy .

二 正則字元簡單介紹

2.1 元字元介紹

   "^" :^會匹配行或者字串的起始位置,有時還會匹配整個文件的起始位置。

   "$"  :$會匹配行或字串的結尾

    如圖

         而且被匹配的字元必須是以This開頭有空格也不行,必須以Regex結尾,也不能有空格與其它字元

 "\b" :不會消耗任何字元只匹配一個位置,常用於匹配單詞邊界 如 我想從字串中"This is Regex"匹配單獨的單詞 "is" 正則就要寫成 "\bis\b" 

    \b 不會匹配is 兩邊的字元,但它會識別is 兩邊是否為單詞的邊界

 "\d": 匹配數字,

    例如要匹配一個固定格式的電話號碼以0開頭前4位後7位,如0737-5686123  正則:^0\d\d\d-\d\d\d\d\d\d\d$ 這裡只是為了介紹"\d"字元,實際上有更好的寫法會在     下面介紹。

 "\w":匹配字母,數字,下劃線.

    例如我要匹配"a2345BCD__TTz" 正則:"\w+"  這裡的"+"字元為一個量詞指重複的次數,稍後會詳細介紹。

 "\s":匹配空格

    例如字元 "a b c" 正則:"\w\s\w\s\w"  一個字元後跟一個空格,如有字元間有多個空格直接把"\s" 寫成 "\s+" 讓空格重複

  ".":匹配除了換行符以外的任何字元

    這個算是"\w"的加強版了"\w"不能匹配 空格 如果把字串加上空格用"\w"就受限了,看下用 "."是如何匹配字元"a23 4 5 B C D__TTz"  正則:".+"

  "[abc]": 字元組  匹配包含括號內元素的字元

        這個比較簡單了只匹配括號記憶體在的字元,還可以寫成[a-z]匹配a至z的所以字母就等於可以用來控制只能輸入英文了,

2.2 幾種反義

  寫法很簡單改成大寫就行了,意思與原來的相反,這裡就不舉例子了

   "\W"   匹配任意不是字母,數字,下劃線 的字元

   "\S"   匹配任意不是空白符的字元

 "\D"  匹配任意非數字的字元

   "\B"  匹配不是單詞開頭或結束的位置

   "[^abc]"  匹配除了abc以外的任意字元

 2.3  量詞

  先解釋關於量詞所涉及到的重要的三個概念

    貪婪(貪心) 如"*"字元 貪婪量詞會首先匹配整個字串,嘗試匹配時,它會選定儘可能多的內容,如果 失敗則回退一個字元,然後再次嘗試回退的過程就叫做回溯,它會每次回退一個字元,直到找到匹配的內容或者沒有字元可以回退。相比下面兩種貪婪量詞對資源的消耗是最大的,

   懶惰(勉強) 如 "?"  懶惰量詞使用另一種方式匹配,它從目標的起始位置開始嘗試匹配,每次檢查一個字元,並尋找它要匹配的內容,如此迴圈直到字元結尾處。

   佔有  如"+" 佔有量詞會覆蓋事個目標字串,然後嘗試尋找匹配內容 ,但它只嘗試一次,不會回溯,就好比先抓一把石頭,然後從石頭中挑出黃金

     "*"(貪婪)   重複零次或更多

     例如"aaaaaaaa" 匹配字串中所有的a  正則: "a*"   會出到所有的字元"a"

     "+"(懶惰)   重複一次或更多次

       例如"aaaaaaaa" 匹配字串中所有的a  正則: "a+"  會取到字元中所有的a字元,  "a+"與"a*"不同在於"+"至少是一次而"*" 可以是0次,

       稍後會與"?"字元結合來體現這種區別

     "?"(佔有)   重複零次或一次

       例如"aaaaaaaa" 匹配字串中的a 正則 : "a?" 只會匹配一次,也就是結果只是單個字元a

   "{n}"  重複n次

       例如從"aaaaaaaa" 匹配字串的a 並重復3次 正則:  "a{3}"  結果就是取到3個a字元  "aaa";

   "{n,m}"  重複n到m次

       例如正則 "a{3,4}" 將a重複匹配3次或者4次 所以供匹配的字元可以是三個"aaa"也可以是四個"aaaa" 正則都可以匹配到

     "{n,}"  重複n次或更多次

       與{n,m}不同之處就在於匹配的次數將沒有上限,但至少要重複n次 如 正則"a{3,}" a至少要重複3次

 把量詞瞭解了之後之前匹配電話號碼的正則現在就可以改得簡單點了^0\d\d\d-\d\d\d\d\d\d\d$ 可以改為"^0\d+-\d{7}$"。

這樣寫還不夠完美如果因為前面的區號沒有做限定,以至於可以輸入很多們,而通常只能是3位或者4位,

現在再改一下 "^0\d{2,3}-\d{7}"如此一來區號部分就可以匹配3位或者4位的了

 2.4 懶惰限定符

  "*?"   重複任意次,但儘可能少重複

      如 "acbacb"  正則  "a.*?b" 只會取到第一個"acb" 原本可以全部取到但加了限定符後,只會匹配儘可能少的字元 ,而"acbacb"最少字元的結果就是"acb"

  "+?"  重複1次或更多次,但儘可能少重複

     與上面一樣,只是至少要重複1次

  "??"  重複0次或1次,但儘可能少重複

      如 "aaacb" 正則 "a.??b" 只會取到最後的三個字元"acb"

  "{n,m}?"  重複n到m次,但儘可能少重複

          如 "aaaaaaaa"  正則 "a{0,m}" 因為最少是0次所以取到結果為空

  "{n,}?"    重複n次以上,但儘可能少重複

          如 "aaaaaaa"  正則 "a{1,}" 最少是1次所以取到結果為 "a"

三  正則進階

     3.1 捕獲分組

  先了解在正則中捕獲分組的概念,其實就是一個括號內的內容 如 "(\d)\d" 而"(\d)" 這就是一個捕獲分組,可以對捕獲分組進行 後向引用 (如果後而有相同的內容則可以直接引用前面定義的捕獲組,以簡化表示式) 如(\d)\d\1 這裡的"\1"就是對"(\d)"的後向引用

那捕獲分組有什麼用呢看個例子就知道了

如  "zery zery" 正則 \b(\w+)\b\s\1\b 所以這裡的"\1"所捕獲到的字元也是 與(\w+)一樣的"zery",為了讓組名更有意義,組名是可以自定義名字的

"\b(?<name>\w+)\b\s\k<name>\b" 用"?<name>"就可以自定義組名了而要後向引用組時要記得寫成 "\k<name>";自定義組名後,捕獲組中匹配到的值就會儲存在定義的組名裡

下面列出捕獲分組常有的用法

"(exp)"    匹配exp,並捕獲文字到自動命名的組裡

"(?<name>exp)"   匹配exp,並捕獲文字到名稱為name的組裡

"(?:exp)"  匹配exp,不捕獲匹配的文字,也不給此分組分配組號

以下為零寬斷言

"(?=exp)"  匹配exp前面的位置

  如 "How are you doing" 正則"(?<txt>.+(?=ing))" 這裡取ing前所有的字元,並定義了一個捕獲分組名字為 "txt" 而"txt"這個組裡的值為"How are you do";

"(?<=exp)"  匹配exp後面的位置

  如 "How are you doing" 正則"(?<txt>(?<=How).+)" 這裡取"How"之後所有的字元,並定義了一個捕獲分組名字為 "txt" 而"txt"這個組裡的值為" are you doing";

"(?!exp)"  匹配後面跟的不是exp的位置

  如 "123abc" 正則 "\d{3}(?!\d)"匹配3位數字後非數字的結果

"(?<!exp)"  匹配前面不是exp的位置

  如 "abc123 " 正則 "(?<![0-9])123" 匹配"123"前面是非數字的結果也可寫成"(?!<\d)123"

四  正則實戰

  正則在做驗證,與資料過濾時體現的威力是巨大的,我想用過的朋友都知道,下面我們把剛剛瞭解的全部結合起來做一次實戰 做資料採集用正則過濾Html標籤並取相應的資料

我們的戰場就選在部落格園吧,假設現在要採集部落格園首頁的所有文章資訊 包括文章標題,連結接 作者部落格地址,文章簡介,文章釋出時間,閱讀資料,評論數 ,推薦數。

先看部落格園文章的Html格式

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<div class="post_item">

<div class="digg">

 <div class="diggit" onclick="DiggIt(3439076,120879,1)">

 <span class="diggnum" id="digg_count_3439076">4</span>

 </div>

 <div class="clear"></div>

 <div id="digg_tip_3439076" class="digg_tip"></div>

</div> 

<div class="post_item_body">

 <h3><a class="titlelnk" href="http://www.cnblogs.com/swq6413/p/3439076.html" target="_blank">分享完整的專案工程目錄結構</a></h3>    

 <p class="post_item_summary">

<a href="http://www.cnblogs.com/swq6413/" target="_blank"><img width="48" height="48" class="pfs" src="http://pic.cnitblog.com/face/142964/20131116170946.png" alt=""/></a> 在專案開發過程中,如何有序的儲存專案中的各類資料檔案,建立一個分類清晰、方便管理的目錄結構是非常重要的。 綜合以前的專案和一些朋友的專案結構,我整理了一份我覺得還不錯的專案目錄結構。 在這裡分享給大家,歡迎各位提出你寶貴的意見和建議。如果喜歡請“推薦”則個,感激萬分!! 整個目錄設定到4級子目錄,實...

 </p>   

 <div class="post_item_foot">    

 <a href="http://www.cnblogs.com/swq6413/" class="lightblue">七少爺</a>

 釋出於 2013-11-23 15:48

 <span class="article_comment"><a href="http://www.cnblogs.com/swq6413/p/3439076.html#commentform" title="2013-11-23 16:40" class="gray">

  評論(4)</a></span><span class="article_view"><a href="http://www.cnblogs.com/swq6413/p/3439076.html" class="gray">閱讀(206)</a></span></div>

</div>

<div class="clear"></div>

</div>

 通過構造一個Http請求來取到資料並對資料進行相應處理得到關鍵資訊,在過濾Html標籤取文章時正則的強大的威力就體現出來了,

正則的知識點也都基本用上了比如 "\s \w+ . * ? "還有捕獲分組,零寬斷言等等。喜歡的朋友可以試一試,然後自己看如何通過正則取相應資料的,程式碼中的正則都是很基本簡單的,其意思與用法都在上文中詳細寫了。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

class Program

 {

  static void Main(string[] args)

  {

   string content = HttpUtility.HttpGetHtml();

   HttpUtility.GetArticles(content);

  }

 }

 internal class HttpUtility

 {

  //預設獲取第一頁資料

  public static string HttpGetHtml()

  {

   HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.cnblogs.com/");

   request.Accept = "text/plain, */*; q=0.01";

   request.Method = "GET";

   request.Headers.Add("Accept-Language", "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");

   request.ContentLength = 0;

   request.Host = "www.cnblogs.com";

   request.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.1 (KHTML, like Gecko) Maxthon/4.1.3.5000 Chrome/26.0.1410.43 Safari/537.1";

   HttpWebResponse response = (HttpWebResponse)request.GetResponse();

   Stream responStream = response.GetResponseStream();

   StreamReader reader = new StreamReader(responStream, Encoding.UTF8);

   string content = reader.ReadToEnd();

   return content;

  }

  public static List<Article> GetArticles(string htmlString)

  {

   List<Article> articleList = new List<Article>();

   Regex regex = null;

   Article article = null;

   regex = new Regex("<div class=\"post_item\">(?<content>.*?)(?=<div class=\"clear\">" + @"</div>\s*</div>)",

        RegexOptions.Singleline);

   if (regex.IsMatch(htmlString))

   {

    MatchCollection aritcles = regex.Matches(htmlString);

    foreach (Match item in aritcles)

    {

     article = new Article();

     //取推薦

     regex =

      new Regex(

       "<div class=\"digg\">.*<span.*>(?<digNum>.*)" + @"</span>" +

       ".*<div class=\"post_item_body\">", RegexOptions.Singleline);

     article.DiggNum = regex.Match(item.Value).Groups["digNum"].Value;

     //取文章標題 需要去除轉義字元

     regex = new Regex("<h3>(?<a>.*)</h3>", RegexOptions.Singleline);

     string a = regex.Match(item.Value).Groups["a"].Value;

     regex = new Regex("<a\\s.*href=\"(?<href>.*?)\".*>(?<summary>.*)</a>", RegexOptions.Singleline);

     article.AritcleUrl = regex.Match(a).Groups["href"].Value;

     article.AritcleTitle = regex.Match(a).Groups["summary"].Value;

     //取作者圖片

     regex = new Regex("<a.*>(?<img><img[^>].*>)</a>", RegexOptions.Singleline);

     article.AuthorImg = regex.Match(item.Value).Groups["img"].Value;

     //取作者部落格URL及連結的target屬性

     regex = new Regex("<a\\s*?href=\"(?<href>.*)\"\\s*?target=\"(?<target>.*?)\">.*</a>",

          RegexOptions.Singleline);

     article.AuthorUrl = regex.Match(item.Value).Groups["href"].Value;

     string urlTarget = regex.Match(item.Value).Groups["target"].Value;

     //取文章簡介

     //1 先取summary Div中所有內容

     regex = new Regex("<p class=\"post_item_summary\">(?<summary>.*)</p>", RegexOptions.Singleline);

     string summary = regex.Match(item.Value).Groups["summary"].Value;

     //2 取簡介

     regex = new Regex("(?<indroduct>(?<=</a>).*)", RegexOptions.Singleline);

     article.AritcleInto = regex.Match(summary).Groups["indroduct"].Value;

     //取釋出人與釋出時間

     regex =

      new Regex(

       "<div class=\"post_item_foot\">\\s*<a.*?>(?<publishName>.*)</a>(?<publishTime>.*)<span class=\"article_comment\">",

       RegexOptions.Singleline);

     article.Author = regex.Match(item.Value).Groups["publishName"].Value;

     article.PublishTime = regex.Match(item.Value).Groups["publishTime"].Value.Trim();

     //取評論數

     regex =

      new Regex(

       "<span class=\"article_comment\"><a.*>(?<comment>.*)</a></span><span class=\"article_view\">",

       RegexOptions.Singleline);

     article.CommentNum = regex.Match(item.Value).Groups["comment"].Value;

     //取閱讀數

     regex = new Regex("<span\\s*class=\"article_view\"><a.*>(?<readNum>.*)</a>", RegexOptions.Singleline);

     article.ReadNum = regex.Match(item.Value).Groups["readNum"].Value;

     articleList.Add(article);

    }

   }

   return articleList;

  }

  public static string ClearSpecialTag(string htmlString)

  {

   string htmlStr = Regex.Replace(htmlString, "\n", "", RegexOptions.IgnoreCase);

   htmlStr = Regex.Replace(htmlStr, "\t", "", RegexOptions.IgnoreCase);

   htmlStr = Regex.Replace(htmlStr, "\r", "", RegexOptions.IgnoreCase);

   htmlStr = Regex.Replace(htmlStr, "\"", "'", RegexOptions.IgnoreCase);

   return htmlStr;

  }

 }

 public class Article

 {

  /// <summary>

  /// 文章標題

  /// </summary>

  public string AritcleTitle { get; set; }

  /// <summary>

  /// 文章連結

  /// </summary>

  public string AritcleUrl { get; set; }

  /// <summary>

  /// 文章簡介

  /// </summary>

  public string AritcleInto { get; set; }

  /// <summary>

  /// 作者名

  /// </summary>

  public string Author { get; set; }

  /// <summary>

  /// 作者地址

  /// </summary>

  public string AuthorUrl { get; set; }

  /// <summary>

  /// 作者圖片

  /// </summary>

  public string AuthorImg { get; set; }

  /// <summary>

  /// 釋出時間

  /// </summary>

  public string PublishTime { get; set; }

  /// <summary>

  /// 推薦數

  /// </summary>

  public string DiggNum { get; set; }

  /// <summary>

  /// 評論數

  /// </summary>

  public string CommentNum { get; set; }

  /// <summary>

  /// 閱讀數

  /// </summary>

  public string ReadNum { get; set; }

 }

 正則部分可能寫得不很完美,但至少也匹配出來了,另外因為自己也是剛接觸正則,也只能寫出這種比較簡單的正則。還望大家海涵~~

五    總結

  正則其實並不難,瞭解每個符號的意思後,自己馬上動手試一試多寫幾次自然就明白了,正則是出了名的坑多,隨便少寫了個點就匹配不到資料了,我也踩了很多坑,踩著踩著就踩出經驗了。

 

 轉載自  https://www.jb51.net/article/97303.htm