相信很多人都碰到過需要用正則匹配字符串的時候,遇到了又不會寫,上網一通亂搜,也看不明白那些鬼畫符是個什麼意思,就當黑箱調來改去的經歷。
但其實如果只是想看懂簡單正則表達式的話,並沒有那麼困難。符號雖多,但仍有線索可尋。它的內容可以大致劃分爲字符組、量詞、分組和斷言四部分
這四個部分的表示方法都和這三個括號()
,[]
,{}
息息相關,簡單的對應關係如下:
- 字符組 []
- 量詞 {}
- 分組 & 斷言 ()
接下來就讓我們以括號爲線索瞭解正則表達式吧
(爲了方便, 文中示例使用JavaScript, 可以直接粘貼到瀏覽器控制檯查看效果)
/bc/.test('abcd') //輸出true 判斷字符串'abcd'是否匹配表達式 'abc' /字符串/是js表示正則的語法
'abcd'.search(/bc/) //輸出1 搜索首次匹配的位置
'abcd'.match(/bc/)[0] //輸出 獲取匹配到的字符串
一. 字符組 []
首先,在沒有特殊符號的情況下,正則只是簡單的匹配字符是否相等
比如 /b/.test('abc')
匹配字符串中的b字符
但是往往我們會需要在一個位置上匹配一類
字符,這在正則中被稱爲字符組,使用 中括號[] 表示
例如,需要查找字符串中是否存在數字的話,可以寫作'a1'.search(/[0123456789]/) //輸出1
意思是字符串’a1’中的下標爲1的字符可以由表達式/[0123456789]/
匹配
但這樣也有個問題,表示英文字母得把26個字母挨個敲一遍,太長了; 不過放心,正則提供了一些簡寫的方式
1. 字符組的簡寫
1.1 範圍表示法
範圍表示法就是以[x-y]
的形式表示x到y範圍的字符,按照ASCII的順序來
這樣,[0123456789]
就可以寫作[0-9]
,小寫字母也就可以表示爲[a-z]
1.2 字符組簡記法
使用[0-9]
,[a-z]
已經可以很方便的表示數字和小寫字母了,但仍然有更簡單的表示,也就是簡記法
比如 表示數字的 [0-9]
可以寫成 \d
,d表示數字(digit); 雖然\d
這種寫法並沒有中括號,但它們是等價的;
下面是一些常用的簡記法和其等價的字符組的對應關係
簡記法 | 等價字符組 | 說明 |
---|---|---|
\d | [0-9] | 數字 digit |
\D | [^0-9] | 非數字 |
\s | [ \t\r\n\v\f] | 空白字符 |
\S | [^ \t\r\n\v\f] | 非空白字符 |
\w | [0-9a-zA-Z_] | 單詞字符(字母數字下劃線,包括中文) |
\W | [^0-9a-zA-Z_] | 非單詞字符 |
2. 排除和轉義
2.1 排除型字符組
上面的表裏一二行出現了一個奇怪的現象,[0-9]
只是加了個^
,怎麼就從數字變成非數字了呢?不應該表示的是十個數字加上一個^
這11個字符嗎?
這個^
符號在字符組裏表示 取反 ,可以很方便的表示指定字符之外的字符集合
這一概念,比如[^0-9]
可以用來形容所有非數字字符
但是這樣的話 十個數字加上一個^
這11個字符 又該怎麼表示呢?
其實,^
只有緊跟在左中括號後面時,才表示取反
的意思,如果改成[0-9^]
,那麼它的意思就只是簡單的匹配 十個數字或者^
字符了
2.2 轉義字符
讓取反符號^
變成普通字符並不只有更改位置這一種方式,還可以通過轉義字符來實現
說到轉義,首先不得不提到一個概念: 元字符 ; 這些字符不同於普通字符,它們在正則表達式中有折特殊的含義,例如 字符組[^0-9]
裏面的^
和 -
,還有外層的中括號本身等;
如果就是想要匹配這些元字符本身,而不是它們所表達的特殊含義的話,可以在它們前面加上反斜槓\
,來恢復它們的本來面目.
到這裏你也應該發現了,\
也是元字符,需要用\\
來表示它本身
下面是一些常見的元字符
元字符 | 作用 | 說明 |
---|---|---|
- | 範圍表示法 [0-9]表示[0123456789],按照ASCII編碼順序 |
只在字符組內有效 |
^ | 排除型字符組 例: [^0-9] 匹配除數字外的其他字符 |
只有緊跟在[ 後纔是元字符[12^] 匹配的是’1’ ,‘2’,’^'這三個普通字符 |
\ | 轉義字符 | 匹配字符\ 本身需要\\ |
() | 分組 | |
[] | 字符組 | |
{} | 量詞 |
二. 量詞 {}
上面所描述的都是匹配單個字符,如果需要匹配多個,比如YYYY-MM-DD
這種格式的日期,只用\d的話是這樣的\d\d\d\d-\d\d-\d\d
,很囉嗦;
正則中這種匹配多個字符的方式叫做量詞,寫作{n,m}
,比如4個數字是\d{4}
整個日期可以寫成\d{4}-\d{2}-\d{2}
量詞的一般形式爲{m,n}
,用於限定{}
前面的元素出現的次數,n和m分別爲出現次數的上下限(閉區間)
量詞 | 說明 |
---|---|
{n} | 必須出現n次 |
{m,n} | 出現m到n次 |
{m,} | 至少出現m次,無上限 |
* | 次數無上下限 , 等價於{0,} |
+ | 至少一次,等價於{1,} |
? | 0次或1次,等價於 {0,1} |
貪婪和非貪婪模式
看到上面的表,你有沒有疑惑: 如果+
意思是匹配 1到無窮次, 那麼如果有很多的字符都可以匹配的話,它是按多的來還是少的來呢?
具體來說, 用\d+
匹配一段數字,是匹配全部還是隻選第一個?
這裏可以在控制檯裏嘗試一下 '1234'.match(/\d+/)[0]
,會發現它匹配上了全部的1234,所以說量詞默認是 貪婪 的,即儘量多的匹配能匹配上的字符
那麼相對應的就有非貪婪模式(匹配儘量少的字符), 它的寫法是在量詞後面加上?
'1234'.match(/\d+/)[0] // 1234
'1234'.match(/\d+?/)[0] // 1
- 可以簡單的理解爲
- 貪婪模式下這個量詞匹配儘量多的字符
- 非貪婪模式匹配儘量少的字符
有意思的是?
本身也是一種量詞,兩個?
連起來表示: 以非貪婪模式匹配前面的元素0或1次,考慮到要匹配儘量少的字符,那就是壓根不匹配
'ab'.match(/ab?/)[0] // ?等價於量詞 {0,1} , 字符b可以匹配也可以不匹配, 因爲是貪婪模式,那就匹配上了
'ab'.match(/ab??/)[0] // 第一個?是量詞 ,第二個? 表示非貪婪模式