瑞士軍刀---正則表達式

簡介

正則表達式,又稱正規表示法、正規表達式、規則表達式、常規表示法(英語:Regular Expression,在代碼中常簡寫為regex、regexp或RE),計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。在很多文本編輯器里,正則表達式通常被用來檢索、替換那些符合某個模式的文本。

說人話就是:對字符串執行模式匹配的強大工具。

Javascript中的正則表達式相較于其他語言來說實現的并不完整,但仍然是十分強大的,能夠幫助我們完成絕大多數模式匹配任務。

定義

在javascript中,正則表達式的定義和數組以及對象的定義一樣有倆種方式。 一種是直接量,另一種是構造函數。

直接量

Javascript可以使用類似Perl的語法定義一個正則表達式

var r = /pattern/flags;

其中pattern是任何簡單或復雜的正則表達式,flags是用以表達正則表達式行為的一個或者多個標識。

flags

  • g 表示全局(global)模式,即模式將被應用于所有字符串
  • i 表示不區分大小寫
  • m 多行模式

舉個栗子:

var reg1 = /ip/g; // 匹配字符串中所有'ip'的實例

var reg2 = /com/gi; // 匹配以'com'結尾的三個字符的組合,不區分大小寫

使用構造函數

Javascript中正則表達式的構造函數為RegExp,即Regular Expression的縮寫,它接收兩個參數,第一個為要匹配的字符串模式,另一個是可選的標識位。

var reg = new RegExp('nice', 'g'); // 匹配字符串中所有的'nice'

注意:第一個參數是一個字符串nice,而不是正則表達式的直接量/nice/

雙重轉義

所有元字符都需要進行雙重轉義

所有元字符都需要進行雙重轉義

所有元字符都需要進行雙重轉義

使用構造函數定義正則表達式,由于構造函數的第一個參數必須是一個字符串,在匹配元字符時,需要雙重轉義。

//  匹配{user}
var reg1 = new RegExp('\{user\}', 'g'); // wrong

// 由于\在字符串中需要轉義為\\,所以,如果要匹配{user},正則表達式需要寫為
var reg1 = new RegExp('\\{user\\}', 'g');

基本概念總結

元字符

在正則表達式的模式中,有一些字符是有特殊含義的,被稱為元字符,如果要匹配元字符,則必須對其進行轉義,如果用構造函數,則要進行雙重轉義。

這些元字符分別是(一共14個):

{ ( [  \ ^ $ |  ? * + . ] ) }

直接量字符

字符 匹配
字母數字字符 自身
\0 查找 NUL 字符。
\n 查找換行符。
\f 查找換頁符。
\r 查找回車符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八進制數 xxx 規定的字符。
\xdd 查找以十六進制數 dd 規定的字符。
\uxxxx 查找以十六進制數 xxxx 規定的 Unicode 字符。

不管一個直接量字符字符代表何種含義,單個字符始終只匹配單個字符

字符類

字符 等價于 匹配
. [^\n\r] 查找單個字符,除了換行和行結束符。
\w [a-zA-Z_0-9] 任意ASCII單字字符,等價于[a-zA-Z0-9_]
\W [^a-zA-Z_0-9] 查找非單詞字符。
\d [0-9] 查找數字。
\D [^0-9] 查找非數字字符。
\s [ \t\n\x0B\f\r] 查找空白字符。
\S [^ \t\n\x0B\f\r] 查找非空白字符。
[] 來表示單個字符有或的關系,如/[bc]/,匹配b或者c
^ 來表示不匹配后面跟著的字符,如/[^bc],/不匹配b或c
- 來表示一個范圍,如/[a-z]/,匹配a-z的字母,可以和負向類結合/^0-9/
組合類: 以上幾種類組合而成的字符類,如/a-z0-9\n/,匹配a-z的字母或者0-9的數字或者換行符

注意:在javascript類不能嵌套,就是不支持聯合類和交叉類,/a-m[p-z]/和/a-m[^b-e]/在JavaScript中是非法的

錨字符

字符 含義
^ 匹配字符串的開頭,在多行檢索中,匹配一行的開頭
$ 匹配字符串的結尾,在多行檢索中,匹配一行的結尾
\b 匹配單詞邊界。
\B 匹配非單詞邊界。

量詞

簡單量詞

常用量詞 {m,n}等價形式 描述
{0,1} 最多出現1次,例如,do(es)? 匹配 do 或 does 中的 do
* {0,} 可能出現,也可能不出現,出現次數無上限,例如,zo* 匹配 z 和 zoo。
+ {1,} 最少出現1次,例如,zo+ 匹配 zo 和 zoo,但不匹配 z

在正則表達式概念中,稱 +、*、?為常用量詞,稱{n,m}形式的為通用量詞。

使用情景舉例

**?的使用
**
情景:美式應用和英式語言的單詞寫法不同,比如traveler和traveller.

var str1 = 'traveler';
var str2 = 'traveller';
var pattern = /travell?er/;
console.log(pattern.test(str1));
console.log(str2.search(str2));

結果打印:
true
0

匹配各類標簽(前端應用)

  • 匹配所以tag: /<[^>]+>/
  • 匹配open tag: /<[/>][>]*>/
  • 匹配close tag: /</[^>]+>/
  • 匹配self-closing tag: /[^>/]+/>/

復雜量詞

  • 貪婪量詞:先匹配整個字符串,如果不匹配則去掉最后一個字符再匹配,直到沒有任何字符。它是從后向前匹配。所有簡單量詞都是貪婪的
  • 惰性量詞:和貪婪量詞相反,即先匹配第一個字符,不匹配則匹配第一第二個字符,直到最后整個字符串。所有簡單量詞后面加?就是惰性的了
  • 支配量詞:只匹配整個字符串一次,不匹配就結束,也稱最懶量詞,所有簡單量詞后面加+就是支配的了,但是javascript不支持,所以它連出場的機會也沒有了。

舉個栗子(complex-quantifiers.js):

var re1 = /.*bbb/g;     //  定義貪婪量詞
var re2 = /.*?bbb/g;    //  定義簡單惰性
//  var re3 = /.*+bbb/g;//支配性,javascript不支持,nodejs報錯
var  str='abbbaabbbaaabbb1234';

console.log(re1.test(str));  //true
console.log(re1.exec(str));  //null
console.log(str.match(re1));  //abbbaabbbaaabbbb

console.log(re2.test(str));  //true
console.log(re2.exec(str));  //aabbb
console.log(str.match(re2)); //abbb,aabbb,aaabbb

貪婪模式詳解

分組

到目前為止,我們只能一個字符到匹配,雖然量詞的出現,能幫助我們處理一排密緊密相連的同類型字符。但這是不夠的,下面該輪到小括號出場了,中括號表示范圍內選擇,大括號表示重復次數。小括號允許我們重復多個字符。

舉個栗子(group.js):

//分組+量詞
console.log(/(dog){2}/.test("dogdog"))//true
//分組+范圍
console.log("baddad".match(/([bd]ad?)*/))//baddad,dad
//分組+分組
console.log("mon and dad".match(/(mon( and dad)?)/))//mon and dad,mon and dad, and dad

優先級

優先級 符號 說明
最高 轉義符
( )、(?: )、(?= )、[ ] 單元和字符類
*、+、?、{n}、{n,}、{m,n} 量詞
^、$、\任何元字符、任何字符 描點
最低 | 替換,選擇

參考加減乘除,先算括號內的記憶方式

RegExp對象

RegExp屬性

global 屬性 | ignoreCase 屬性 | multiline 屬性 | source 屬性

  • global: 返回布爾值,該值指示使用正則表達式的 global 標志 (g) 的狀態。 默認值為 false。 只讀。
  • ignoreCase: 返回布爾值,該值指示在正則表達式中使用的 ignoreCase 標志 (i) 的狀態。 默認值為 false。只讀。
  • multiline: 返回布爾值,該值指示在正則表達式中使用的 multiline 標志 (m) 的狀態。 默認值為 false。 只讀。
  • source: 返回正則表達式模式的文本的一個副本。 只讀。
  • lastIndex: 如果使用了全局模式,這個變量保存的是字符串中嘗試下次匹配的偏移值。在exec和test中都會用到這個變量。 可寫

RegExp方法

RegExp 對象有 3 個方法:test()、exec() 以及 compile()

RegExp.test(string)

test 方法檢查字符串中是否存在某種模式,如果存在,則返回 true,否則返回 false。test 方法不修改全局 RegExp 對象的屬性。

語法:

rgExp.test(str) 

//  參數
rgExp 必需。 包含正則表達式模式和適用標志的 Regular Expression 對象的實例。
str 必需。 將對其執行搜索的字符串。

栗子:

var str = 'huangge is an handsome man';

var reg1 = new RegExp( 'handsome', 'g' );
var result = reg1.test(str);
console.log('huangge is an handsome man: ' + result);
RegExp.exec(string)

exec 方法使用正則表達式模式對字符串執行需找匹配,如果成功,則返回表示匹配信息的數組,否則返回null。默認情況下,它返回第一次匹配的結果

語法:

rgExp.exec(str) 

//  參數
rgExp 必需。 包含正則表達式模式和適用標志的 Regular Expression 對象的實例。
str 必需。 對其執行搜索的 String 對象或字符串文本。

返回值

如果成功匹配,exec 方法返回一個數組,并且更新正則表達式對象的屬性。返回的數組包括匹配的字符串作為第一個元素,緊接著一個元素對應一個成功匹配被捕獲的字符串的捕獲括號(capturing parenthesis)。

如果匹配失敗,exec 方法將返回 null。

栗子(regexp-exec.js):

var reg= /\d{4}-\d{2}-\d{2}/g;
var str = 'aaa2015-08-11aaaaaabbss2015-08-22bbb';
var result = reg.exec(str);
console.log(result);


結果如下:

[ '2015-08-11',
  index: 3,
  input: 'aaa2015-08-11aaaaaabbss2015-08-22bbb' ]
RegExp.compile(string)

將正則表達式編譯為內部格式,從而更快地執行。

語法:

rgExp.compile(pattern, [flags]) 

//  參數
rgExp 必需。 Regular Expression 對象的一個實例。 可以是變量名或文本。
pattern 必需。 一個字符串表達式,包含要編譯的正則表達式模式
flags 可選。 可以組合使用的可用標志有:
    g(全局搜索出現的所有模式)
    i(忽略大小寫)
    m(多行搜索)

compile 方法將 pattern 轉換為內部的格式,從而執行得更快。 例如,這允許在循環中更有效地使用正則表達式。 當重復使用相同的表達式時,編譯過的正則表達式使執行加速。 然而,如果正則表達式發生更改,則這種編譯毫無益處。

String的正則方法

String.match(RegExp)

這個方法類似RegExp.exec(string),只是調換了RegExp和string的位置。 另一個區別就是,無論是否指定全局,RegExp.exec(string)只是返回單詞匹配結果。 而string.match()會返回一個字符串數組,其中包含了各次匹配成功的文本

舉個栗子(string-match.js):

var reg= /\d{4}-\d{2}-\d{2}/g;
var str = 'aaa2015-08-11aaaaaabbss2015-08-22bbb';
var result = str.match(reg);
console.log(result);

打印的結果為:

[ '2015-08-11', '2015-08-22' ]
String.search(RegEexp)

這個用來尋找某個正則表達式在字符串第一次匹配成功的位置,如果不成功,則返回-1

舉個栗子(string-search.js):

var str1 = 'sssssaa111';
var str2 = 'sssssaaaaa';

var pattern = /\d/;
console.log ('st1  ' + str1.search(pattern));
console.log ('st2  ' + str2.search(pattern));

打印的結果為:

st1  7
st2  -1
String.replace(RegExp,replacement)

這個方法用來正則表達式替換。 將匹配到的文本替換為replacement。默認情況替換一次。如果設定了全局模式,則所有能匹配的文本都會替換。
舉個栗子(string-replace.js):

var reg = /\d{4}-\d{2}-\d{2}/;
var str = '2015-08-11 2015-08-22';

console.log(str.replace(reg, 'Date:'));

打印的結果為:

ate: 2015-08-22
String.split(RegExp [,limit])

這個方法使用一個正則表達式切分字符串,正則表達式是否使用全局對結果沒有影響
舉個栗子(string-split.js):

var str = 'one two  three    four';
var pattern = /\s+/;
console.log(str.split(pattern))

打印的結果為:

[ 'one', 'two', 'three', 'four' ]

常用的正則表達式

  • 匹配中文字符的正則表達式: [\u4e00-\u9fa5]

  • 匹配雙字節字符(包括漢字在內):[^\x00-\xff]
    評注:可以用來計算字符串的長度(一個雙字節字符長度計2,ASCII字符計1)

  • 匹配空白行的正則表達式:\n\s*\r

  • 匹配HTML標記的正則表達式:<(\S?)[^>]>.?</\1>|<.? />

  • 匹配首尾空白字符的正則表達式:^\s|\s$

  • 匹配Email地址的正則表達式:\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*

  • 匹配網址URL的正則表達式:[a-zA-z]+://[^\s]*

  • 匹配帳號是否合法(字母開頭,允許5-16字節,允許字母數字下劃線):^[a-zA-Z][a-zA-Z0-9_]{4,15}$

  • 匹配國內電話號碼:\d{3}-\d{8}|\d{4}-\d{7}

  • 匹配騰訊QQ號:[1-9][0-9]{4,}

  • 匹配中國郵政編碼:[1-9]\d{5}(?!\d)

  • 匹配身份證:\d{15}|\d{18}

  • 匹配ip地址:\d+.\d+.\d+.\d+

匹配特定數字

^[1-9]\d*$    //匹配正整數
^-[1-9]\d*$   //匹配負整數
^-?[1-9]\d*$   //匹配整數
^[1-9]\d*|0$  //匹配非負整數(正整數 + 0)
^-[1-9]\d*|0$   //匹配非正整數(負整數 + 0)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$   //匹配正浮點數
^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$  //匹配負浮點數
^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$  //匹配浮點數
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$   //匹配非負浮點數(正浮點數 + 0)
^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$  //匹配非正浮點數(負浮點數 + 0)

匹配特定字符串

^[A-Za-z]+$  //匹配由26個英文字母組成的字符串
^[A-Z]+$  //匹配由26個英文字母的大寫組成的字符串
^[a-z]+$  //匹配由26個英文字母的小寫組成的字符串
^[A-Za-z0-9]+$  //匹配由數字和26個英文字母組成的字符串
^\w+$  //匹配由數字、26個英文字母或者下劃線組成的字符串
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容