正則表達式快速入門(概括版)

轉載請註明出處 來源:paraller's blog
原文排版鏈接: 點擊跳轉 html

想系統的學習正則表達式,在網上找了不少教程,其中《55分鐘學會正則表達式》這個翻譯自外網的教程講的最系統詳細,學完整個正則表達式的耗時大概在兩個多小時的樣子。
爲何寫這篇文章,由於我以爲若是不是這篇文章太生硬,廢話比較多,排版亂,有些地方存在錯誤的話,我以爲55分鐘就學完了。因此從新整理排版了一次。linux

學習的工具是 cat log | grep -E 'pattern'git

正則表達式的基礎語法

核心知識點
  • 正則表達式由只表明自身的字面值和表明特定含義的元字符組成。
  • 字面值(Literals): 意味着它們查找的是自身
  • 元字符:表明一些模式匹配
句點 .
  • 一個.表示匹配任何單個字符。下面這個正則表達式c.t表明: 先找到c,接着找到任何單個字符,再找到t。
  • 將會找到cat,cot ,甚至字面值爲c.t的字符串,可是不包括ct或者coot。
反斜槓 \
  • 任何元字符若是用一個反斜杆進行轉義就會變成字面值。因此上述的正則表達式c\.t ,可以匹配文本 c.t
  • 文本a\h,能夠經過 a\\h找到
字符類( Character classes )
  • 定義: 字符在方括號中的集合。表示:找到其中任意的字符
  • 正則表達式c[aeiou]t表示「找到c後跟一個元音字母,再找到t」。將會匹配到cat
  • 正則表達式[0123456789]表示找到一個數字
  • 正則表達式[a]a意義相同:「找到a」

一些轉義的例子:github

  • \[a\] 表示文本 [a]
  • [\[\]ab] 表示匹配一個 [ 或者 ] 或者 a 或者 b
  • [\\[\]] 表示「匹配一個\ 或者 [ 或者 ]

note:正則表達式

  • 一些字符在字符類內部扮演着元字符的角色,但在字符類外部則充當字面值。好比:連字符 -
  • 一些字符作着相反的事。好比: .表示「匹配任意字符」,可是[.]表示「匹配句點」
  • 一些字符在兩種情形都爲元字符,但在各自情形裏表明不一樣的含義。
字符類區間(ranges)

你能夠在字符類中使用連字符來表示一個字母或數字的區間_:編程

  • [b-f][bcdef] 一個意思
  • [A-Z][ABCDEFGHIJKLMNOPQRSTUVWXYZ] 都表示「匹配大寫字母」。
  • [1-9][123456789] 都表示「匹配一個非零數字」。

區間和單獨的字符,可能會共存於同一個字符類:segmentfault

  • [0-9.,]表示「匹配 一個數字 或者 一個. 或者 一個,」。
  • [0-9a-fA-F]表示「匹配一個十六進制數」。
  • [a-zA-Z0-9\-]表示「匹配 一個字母數字字符 或 -」。

Note:編程語言

  • 你能夠嘗試在區間內以非字母數字字符結束(好比abc[!-/]),但這在其它實現中的語法不必定對。即便語法正確,但在這個區間內很難看出包含了哪一個字符。請不要這麼幹
  • 一樣的,區間端點的範圍應該一致。即便像[A-z]這種表達式在你選擇的實現中合法,但它作的可能會與你想法用出入。
  • 注意。 區間是字符的區間,不是數字的區間。正則表達式[1-31]表示 找到一個1或一個 2或一個3,不是找到一個從1到31的整數
字符類的否認(negation)

你能夠經過在最開始的位置使^來排除一個字符類。編輯器

  • [^a]表示「匹配除了a的任意字符」。
  • [^a-zA-Z0-9]表示「找到一個非字母數字字符」。
  • [\^abc]表示「找到一個^ 或者 a 或者 b 或者 c」。
  • [^\^]表示「找到除了^的任意字符」
字符類補充
  • \d 含義與[0-9]一致:「匹配一個數字」。
  • \w 的含義與[0-9A-Za-z_]一致:「匹配一個單詞字符,( 字母或數字或下劃線或漢字)」。
  • \s 表示「匹配任意空白字符(空格,tab)」。
  • \s+ 能夠表示回車和換行
  • \D[^0-9]:「匹配任意非數字的字符」。
  • \W[^0-9A-Za-z_]:「匹配任意非單詞字符(譯者注:匹配任意不是字母,數字,下劃線,漢字的字符)」。
  • \S 表示「匹配任意不是空白符的字符」。
  • \t 表示製表符
  • [\u4e00-\u9fa5] 表示中文和英文
  • [^\x00-\xff] 匹配雙字節字符,中文也是雙字節的字符,不包括英文
乘法器(Multipliers)

能夠在字面值或者字符類後跟着一個大括號來使用乘法器。工具

  • 正則表達式a{1}同a,表示「匹配一個a」。
  • \d{3} 表示3個相連的數字。
  • a{0} 表示「匹配空字符」。
  • a\{2\} 表明文本 a{2}
  • 在字符類中大括號沒有特別的含義。[{}]表明「匹配一個{ 或者 }
  • 乘法器沒有記憶。[abc]{2}表示「匹配a或者b或者c,接着匹配a或者b或者c。這跟匹配aabbcc含義不一樣
乘法器區間
  • x{4,4} 跟x{4}同樣。
  • colou{0,1}r 表示「匹配colour或color。
  • a{3,5} 表示「匹配aaaaa或aaaa或aaa」。
  • a{1,} 表示「在一列中找到一個或多個a」。然而你的乘法器將會是貪婪的。在找到第一個a後,它將會盡量匹配到更多的a。
  • .{0,} 表示「匹配任何情形」。無論你的輸入文本是什麼——甚至爲空

注意:

  • 優先選擇更長的匹配,由於乘法器是貪婪的。若是你輸入的文本是I had an aaaaawful day,該正則表達式就會在aaaaawful中匹配到aaaaa。不會在第三個a後就中止匹配。
  • 乘法器在找到第一個文本的時候就會中止,若是你的輸入文本爲I had an aaawful daaaaay,以後這個正則表達式會在第一次的匹配中於aaawful找到aaa。只有在你說「給我找到另外一個匹配」的時候,它纔會繼續搜索而後在daaaaay中找到aaaaa
乘法器補充
  • ?表明的含義與{0,1}相同。好比說,colou?r表示「匹配colour或color」。
  • *等於{0,}。好比說,*表示「匹配一切」,跟上面提到的同樣。
  • +等於{1,}。好比說,\w+表示至少匹配一個或以上的單詞。 [0-9]+表明至少匹配一個或以上數字
  • \?\*\+表示?*+
  • [?*+]表示找到一個?或者一個*或者一個+
惰性(Non-greed)

前面說到乘法器是貪婪的,能夠經過添加?來消除貪婪特性

  • \d{4,5}? 就會等於 \d{4} ,在找到合適的文本以後就停下來
  • ".*?"表示「匹配一個雙引號,跟着一個儘量少的字符,再跟着一個雙引號」。這實際上頗有用
分支(Alternation)

你可使用管道符號來實現匹配多種選擇:

  • cat|dog表示「匹配cat或dog」。
  • red|blue|red||blue以及|red|blue 都是一樣的意思,「匹配red或blue或空字符串」。
  • a|b|c[abc]同樣。
  • cat|dog|\|表示「匹配cat或dog或管道符號」。
  • [cat|dog]表示「找到a或c或d或d或g或o或t或一個管道符號」。
組合(Grouping)
  • 在一週中找到一天,使用(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day
  • (\w*)ility等同於\w*ility。都表示「找到以ility結尾的單詞」。爲何第一種形式更有用,後面會看到...
  • \(\)表示「匹配一個(後,再匹配一個)」。
  • [()]表示「匹配一個( 或 一個)」。
單詞邊界(Word boundaries)

單詞邊界是一個單詞字符和非單詞字符之間的位置。記住,一個單詞字符是w,它是[0-9A-Za-z_],一個非單詞字符是W,也就是1

文本的開頭和結尾老是看成單詞邊界。輸入的文本it's a cat有八個單詞邊界。若是咱們在cat後追加一個空格,這裏就會有九個單詞邊界。

  • 正則表達式\b表示「匹配一個單詞邊界」。
  • \b\w\w\w\b表示「匹配一個三個字母的單詞」。
  • a\ba表示「找到a,跟着一個單詞邊界,接着找到a」。無論輸入文本是什麼,這個正則表達式永遠都不會成功找到一個匹配。
  • 單詞邊界不是字符。它們寬度爲零.下面的正則表達式表示相同的含義:
行邊界(Line boundaries)

每一塊文本會分解成一個或多個行,用換行符分隔,像這樣:

  • 正則表達式^表示「匹配開始行」。
  • 正則表達式$表示「匹配結束行」。
  • ^$表示「匹配空行」。
  • ^.*$將會匹配整個文本,由於換行符是一個字符,因此.會匹配它。爲了匹配單行,要使用惰性乘法器,^.?$ , ^.*?$
  • \^\$表示「匹配尖符號後跟着一個美圓符號」。
  • [$]表示「匹配一個美圓符」。然而,[^]是非法單正則表達式。要記住的是尖符號在方括號中時有不一樣的特殊含義。把尖符號放在字符類中,這麼用[\^]
  • 像單詞邊界同樣,行邊界也不是字符。它們寬度爲零。

捕獲和替換

這裏就是正則表達式開始變得異常強大的地方。

捕獲組

你已經知道,括號是用來表示組。它們也能夠用來捕獲子串。若是正則表達式是一個很小的電腦程序,這個捕獲組就是它的輸出(的一部分)。

正則表達式(\w*)ility表示「找到一個以ility結束的單詞」。捕獲組1就是匹配了部份內容的\w*

  • 文本包含單詞accessibility,捕獲組1就是accessib
  • 文本只包含ility,捕獲組1就是空字符串。

你能夠擁有多個捕獲組,它們甚至能夠嵌套使用。捕獲組從左到右進行編號。只要計算左圓括號。

假設咱們到正則表達式是(\w+) had a ((\w+) \w+)。若是咱們的輸入文本是I had a nice day,那麼:
捕獲組1是I
捕獲組2是nice day
捕獲組3是nice

在一些實現中,你可能能夠訪問捕獲組0,即完整匹配:I had a nice day

是的,這確實意味着圓括號有些重複。從一個成功返回的匹配中捕獲組數量老是等於原來正則表達式中捕獲組的數量。記住這一點,由於它能夠幫助你理解一些使人困惑的情形。
正則表達式((cat)|dog)表示「匹配cat或dog」。這裏老是存在兩組捕獲組。若是咱們的輸入文本是dog,那麼捕獲組1是dog,捕獲組2是空字符串,由於另外一個選擇未被使用

正則表達式a(\w)*表示「 匹配一個以a開頭的單詞」。這裏老是隻有一個捕獲組(譯者注:除去捕獲組0):

  • 若是輸入文本是a,捕獲組1是空字符串。
  • 若是輸入文本是ad,捕獲組1是d
  • 若是輸入文本是avocado,捕獲組1是vocado
替換

一旦你用了正則表達式來查找字符串,你能夠指定另外一個字符串來替換它。第二個字符串時替換表達式

你能夠在你的替換表達式中引用捕獲組。這是你能夠在替換表達式惟一能的特殊的事,它意味着你沒必要徹底銷燬你剛剛發現的東西。

比方說,你嘗試去用ISO 8691格式的日期(YYYY-MM-DD)去替換美式日期(MM/DD/YY)。

  • 經過正則表達式(\d\d)/(\d\d)/(\d\d)開始。注意這裏有三個捕獲組:月,日和兩個數字表示的年。
  • 經過使用一個\和一個捕獲組號來引用一個捕獲組。因此,你的替換表達式爲20\3-\1-\2
  • 若是咱們的輸入文本是03/04/05(表示 3月4號,2005年),那麼:

    • 捕獲組1是03;
    • 捕獲組2是04;
    • 捕獲組3是05;
  • 你能夠在替換表達式中屢次引用捕獲組。
  • 在替換表達式中的反斜杆必須進行轉義。舉個例子,你有一些在計算機程序的字面值中使用的文本。那就意味着你須要在普通文本中的每一個雙引號或者反斜杆前放置一個反斜杆。
  • 正則表達式([\"])中,捕獲組1是"或者\
  • 替換表達式\\\1中,一個字面值反斜杆後跟着一個匹配的雙引號或者反斜杆。
後向引用(Back-references)
  • 你能夠在一樣的表達式中引用同一個捕獲組。這稱爲後向引用
  • 表達式([abc])\1表示匹配aabbcc
結合正則表達式編程

過分反斜線綜合徵(Excessive backslash syndrome)

在一些編程語言中,如Java,對於含有正則表達式的字符串沒有提供特別的支持。字符串有本身的轉義規則,這些規則與正則表達式的轉義規則疊加,一般會致使反斜杆過多(overload)。好比(仍是Java):

  • 爲了匹配一個數字,正則表達式\d在源代碼中變成String re = "\d;"
  • 爲了匹配一個雙引號字符串,"[^"]*"變成String re = "\"[^\"]*\"";。
  • 爲了匹配一個反斜杆或者一個左方括號或者一個又方括號,正則表達式[\\\\[\\]]變成String re = "[\\\\\\[\\]]";。
  • String re = "\s";和String re = "[ ]";是同樣的。注意不一樣的轉義「優先級」。

在其它編程語言裏,經過一個特殊標記來標識正則表達式,一般是正斜杆/。這裏有一些JavaScript例子:

  • 爲了匹配一個數字,\d變成var regExp = /\d/;。
  • 匹配一個反斜杆或者一個左方括號或者一個右方括號,var regExp = /[\\\\[\\]]/;。
  • var regExp = /\s/;和var regExp = /[ ]/;同樣。
  • 固然,這意味着必須對正斜槓而不是雙引號進行轉義。匹配URL的前面部分:var regExp = /https?:\/\//;。

基於這一點,我但願你明白爲何我對你反覆說起反斜杆。

練習

題目以下:

  • 將文本中的全部 中文 替換成 中文
  • 編寫一個正則表達式匹配1到31(含)之間的整數。
  • 雙引號內全部不包含 " 的文本
  • 雙引號內全部文本

答案:

## Find:
(_)([^\x00-\xff]+)+(_)
## Replace
`\*\*\2\*\*`
[1-9]|[12][0-9]|3[01]
cat log | grep -E '"[^"]{0,}"'
cat log | grep -E '".{0,}"'

練習草稿

I had a nice day

a,a
a!b
90
"i love you , mary!"
"i love you","hey"
food
z...z
a b     c
d       g
e
iec ieac
c[abc]at
2016-10-22 12:12:12
cat
c.t
c\.t  [a]

其餘

偏移量(Offsets)

在文本編輯器中,會在你光標所在處開始搜索。這個編輯器會向前開始搜索文字,而後停在第一個匹配的地方。下一次搜索會在第一次完成搜索的地方的右側開始。

當編程的時候,文本的_偏移量_必須的。這個偏移量會在代碼中有明確的支持,或保存在包含文本的對象中(如Perl),或包含正則表達式的對象中(如JavaScirpt)。(在Java裏,這是一個由正則表達式和複合對象的字符串。)在任何狀況下,默認值> 0,表示文本的開始。搜索後,偏移量會自動更新,或者做爲輸出的一部分返回。

不管什麼狀況,一般很容易去使用循環來解決這個問題。

注意正則表達式匹配空字符串是徹底可能的。 你能夠立馬實現的一個簡單的例子是a{0}在這種狀況下,新的偏移量等於舊偏移量,從而致使死循環。

一些實現可能保護你避免發生這些狀況,但要查下對應的文檔。

動態正則表達式

動態地構造一個正則表達式字符串時必定要當心。若是你使用的字符串不是固定的,那麼它可能包含意想不到的元字符。這會致使語法錯誤。更糟糕的是,它可能產生一個語法正確,但行爲不可預期的正則表達式。

有bug的Java代碼:

String sep = System.getProperty("file.separator");
String[] directories = filePath.split(sep);

這個bug就是:String.split()認爲sep是一個正則表達式。可是在Windows下,sep是由犯斜杆組成的字符串\.這不是一個語法正確的正則表達式。結果是:一個異常PatternSyntaxException。

任何一個優秀的編程語言都提供了一種機制,用以轉義在一個字符串中出現的全部元字符。在Java中,你能夠這麼作:

String sep = System.getProperty("file.separator");
String[] directories = filePath.split(Pattern.quote(sep));
郵件地址

不要使用正則表達式來驗證郵件地址。

首先,這很難保證正確無誤。電子郵件地址確實符合一個正則表達式,可是這個表達式長又複雜地讓人聯想到世界末日。任何縮略都會可能產生遺漏(false negatives)。(你知道嗎?電子郵件地址能夠包含註釋!)

其次,即便所提供的電子郵件地址符合正則表達式,但也並不能證實它的存在。驗證電子郵件地址的惟一方法是發送電子郵件給它。

標記

在正式的應用中,不要使用正則表達式來解析HTML或XML。解析HTML/XML是

不可能使用簡單的正則
通常來講很難
一個已解決了的問題。
不妨找一個已有的解析庫來爲你搞定這些工做。

一些場景

替換
Host    segmentfault.com
Connection  keep-alive
Content-Length  55
Accept  */*
Origin  https://segmentfault.com
X-Requested-With    XMLHttpRequest
User-Agent  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
^(\S+)(\s+)(.{0,})
"\1":"\3",
"Host":"segmentfault.com",
"Connection":"keep-alive",
"Content-Length":"55",
"Accept":"*/*",
"Origin":"https://segmentfault.com",
"X-Requested-With":"XMLHttpRequest",
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
文本塊 換行匹配
---
layout:     post
tags:
    - linux
---


---(\n(.{0,}))*---

參考網站

55分鐘學會正則表達式(譯)


  1. 0-9A-Za-z_
相關文章
相關標籤/搜索