亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

正則表達(dá)式前端使用手冊(cè)

zhoutao / 3499人閱讀

摘要:非貪婪模式盡可能少的匹配所搜索的字符串,而默認(rèn)的貪婪模式則盡可能多的匹配所搜索的字符串。

導(dǎo)讀

你有沒有在搜索文本的時(shí)候絞盡腦汁, 試了一個(gè)又一個(gè)表達(dá)式, 還是不行.

你有沒有在表單驗(yàn)證的時(shí)候, 只是做做樣子(只要不為空就好), 然后燒香拜佛, 虔誠(chéng)祈禱, 千萬不要出錯(cuò).

你有沒有在使用sed 和 grep 命令的時(shí)候, 感覺莫名其妙, 明明應(yīng)該支持的元字符, 卻就是匹配不到.

甚至, 你壓根沒遇到過上述情況, 你只是一遍又一遍的調(diào)用 replace 而已 (把非搜索文本全部替換為空, 然后就只剩搜索文本了), 面對(duì)別人家的簡(jiǎn)潔高效的語句, 你只能在心中吶喊, replace 大法好.

為什么要學(xué)正則表達(dá)式. 有位網(wǎng)友這么說: 江湖傳說里, 程序員的正則表達(dá)式和醫(yī)生的處方, 道士的鬼符齊名, 曰: 普通人看不懂的三件神器. 這個(gè)傳說至少向我們透露了兩點(diǎn)信息: 一是正則表達(dá)式很牛, 能和醫(yī)生的處方, 道士的鬼符齊名, 并被大家提起, 可見其江湖地位. 二是正則表達(dá)式很難, 這也從側(cè)面說明了, 如果你可以熟練的掌握并應(yīng)用它, 在裝逼的路上, 你將如日中天 (別問我中天是誰……) !

顯然, 有關(guān)正則表達(dá)的介紹, 無須我多言. 這里就借助 Jeffrey Friedl 的《精通正則表達(dá)式》一書的序言正式拋個(gè)磚.

? "如果羅列計(jì)算機(jī)軟件領(lǐng)域的偉大發(fā)明, 我相信絕對(duì)不會(huì)超過二十項(xiàng), 在這個(gè)名單當(dāng)中, 當(dāng)然應(yīng)該包括分組交換網(wǎng)絡(luò), Web, Lisp, 哈希算法, UNIX, 編譯技術(shù), 關(guān)系模型, 面向?qū)ο? XML這些大名鼎鼎的家伙, 而正則表達(dá)式也絕對(duì)不應(yīng)該被漏掉.

? 對(duì)很多實(shí)際工作而言, 正則表達(dá)式簡(jiǎn)直是靈丹妙藥, 能夠成百倍的提高開發(fā)效率和程序質(zhì)量, 正則表達(dá)式在生物信息學(xué)和人類基因圖譜的研究中所發(fā)揮的關(guān)鍵作用, 更是被傳為佳話. CSDN的創(chuàng)始人蔣濤先生在早年開發(fā)專業(yè)軟件產(chǎn)品時(shí), 就曾經(jīng)體驗(yàn)過這一工具的巨大威力, 并且一直印象深刻."

因此, 我們沒有理由不去了解正則表達(dá)式, 甚至是熟練掌握并運(yùn)用它.

本文以正則基礎(chǔ)語法開篇, 結(jié)合具體實(shí)例, 逐步講解正則表達(dá)式匹配原理. 代碼實(shí)例使用語言包括 js, php, python, java(因有些匹配模式, js并未支持, 需要借助其他語言講解). 內(nèi)容包括初階技能和高階技能, 適合新手學(xué)習(xí)和進(jìn)階. 本文力求簡(jiǎn)單通俗易懂, 同時(shí)為求全面, 涉及知識(shí)較多, 共計(jì)12k字, 篇幅較長(zhǎng), 請(qǐng)耐心閱讀, 如有閱讀障礙請(qǐng)及時(shí)聯(lián)系我.

回顧歷史

要論正則表達(dá)式的淵源, 最早可以追溯至對(duì)人類神經(jīng)系統(tǒng)如何工作的早期研究. Warren McCulloch 和 Walter Pitts 這兩位神經(jīng)大咖 (神經(jīng)生理學(xué)家) 研究出一種數(shù)學(xué)方式來描述這些神經(jīng)網(wǎng)絡(luò).

1956 年, 一位叫 Stephen Kleene 的數(shù)學(xué)家在 McCulloch 和 Pitts 早期工作的基礎(chǔ)上, 發(fā)表了一篇標(biāo)題為"神經(jīng)網(wǎng)事件的表示法"的論文, 引入了正則表達(dá)式的概念.

隨后, 發(fā)現(xiàn)可以將這一工作應(yīng)用于使用 Ken Thompson 的計(jì)算搜索算法的一些早期研究中. 而 Ken Thompson 又是 Unix 的主要發(fā)明人. 因此半個(gè)世紀(jì)以前的Unix 中的 qed 編輯器(1966 qed編輯器問世) 成了第一個(gè)使用正則表達(dá)式的應(yīng)用程序.

至此之后, 正則表達(dá)式成為家喻戶曉的文本處理工具, 幾乎各大編程語言都以支持正則表達(dá)式作為賣點(diǎn), 當(dāng)然 JavaScript 也不例外.

正則表達(dá)式的定義

正則表達(dá)式是由普通字符和特殊字符(也叫元字符或限定符)組成的文字模板. 如下便是簡(jiǎn)單的匹配連續(xù)數(shù)字的正則表達(dá)式:

/[0-9]+/
/d+/

"d" 就是元字符, 而 "+" 則是限定符.

元字符
元字符 描述
. 匹配除換行符以外的任意字符
d 匹配數(shù)字, 等價(jià)于字符組[0-9]
w 匹配字母, 數(shù)字, 下劃線或漢字
s 匹配任意的空白符(包括制表符,空格,換行等)
 匹配單詞開始或結(jié)束的位置
^ 匹配行首
$ 匹配行尾
反義元字符
元字符 描述
D 匹配非數(shù)字的任意字符, 等價(jià)于[^0-9]
W 匹配除字母,數(shù)字,下劃線或漢字之外的任意字符
S 匹配非空白的任意字符
B 匹配非單詞開始或結(jié)束的位置
[^x] 匹配除x以外的任意字符

可以看出正則表達(dá)式嚴(yán)格區(qū)分大小寫.

重復(fù)限定符

限定符共有6個(gè), 假設(shè)重復(fù)次數(shù)為x次, 那么將有如下規(guī)則:

限定符 描述
* x>=0
+ x>=1
? x=0 or x=1
{n} x=n
{n,} x>=n
{n,m} n<=x<=m
字符組

[...] 匹配中括號(hào)內(nèi)字符之一. 如: [xyz] 匹配字符 x, y 或 z. 如果中括號(hào)中包含元字符, 則元字符降級(jí)為普通字符, 不再具有元字符的功能, 如 [+.?] 匹配 加號(hào), 點(diǎn)號(hào)或問號(hào).

排除性字符組

[^…] 匹配任何未列出的字符,. 如: [^x] 匹配除x以外的任意字符.

多選結(jié)構(gòu)

| 就是或的意思, 表示兩者中的一個(gè). 如: a|b 匹配a或者b字符.

括號(hào)

括號(hào) 常用來界定重復(fù)限定符的范圍, 以及將字符分組. 如: (ab)+ 可以匹配abab..等, 其中 ab 便是一個(gè)分組.

轉(zhuǎn)義字符

即轉(zhuǎn)義字符, 通常 * + ? | { [ ( ) ] }^ $ . # 和 空白 這些字符都需要轉(zhuǎn)義.

操作符的運(yùn)算優(yōu)先級(jí)

轉(zhuǎn)義符

(), (?:), (?=), [] 圓括號(hào)或方括號(hào)

*, +, ?, {n}, {n,}, {n,m} 限定符

^, $ 位置

| "或" 操作

測(cè)試

我們來測(cè)試下上面的知識(shí)點(diǎn), 寫一個(gè)匹配手機(jī)號(hào)碼的正則表達(dá)式, 如下:

(+86)?1d{10}

① "+86" 匹配文本 "+86", 后面接元字符問號(hào), 表示可匹配1次或0次, 合起來表示 "(+86)?" 匹配 "+86" 或者 "".

② 普通字符"1" 匹配文本 "1".

③ 元字符 "d" 匹配數(shù)字0到9, 區(qū)間量詞 "{10}" 表示匹配 10 次, 合起來表示 "d{10}" 匹配連續(xù)的10個(gè)數(shù)字.

以上, 匹配結(jié)果如下:

修飾符

javaScript中正則表達(dá)式默認(rèn)有如下五種修飾符:

g (全文查找), 如上述截圖, 實(shí)際上就開啟了全文查找模式.

i (忽略大小寫查找)

m (多行查找)

y (ES6新增的粘連修飾符)

u (ES6新增)

常用的正則表達(dá)式

漢字: ^[u4e00-u9fa5]{0,}$

Email: ^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$

URL: ^https?://([w-]+.)+[w-]+(/[w-./?%&=]*)?$

手機(jī)號(hào)碼: ^1d{10}$

身份證號(hào): ^(d{15}|d{17}(d|X))$

中國(guó)郵政編碼: [1-9]d{5}(?!d) (郵政編碼為6位數(shù)字)

密碼驗(yàn)證

密碼驗(yàn)證是常見的需求, 一般來說, 常規(guī)密碼大致會(huì)滿足規(guī)律: 6-16位, 數(shù)字, 字母, 字符至少包含兩種, 同時(shí)不能包含中文和空格. 如下便是常規(guī)密碼驗(yàn)證的正則描述:

var reg = /(?!^[0-9]+$)(?!^[A-z]+$)(?!^[^A-z0-9]+$)^[^su4e00-u9fa5]{6,16}$/;
正則的幾大家族 正則表達(dá)式分類

在 linux 和 osx 下, 常見的正則表達(dá)式, 至少有以下三種:

基本的正則表達(dá)式( Basic Regular Expression 又叫 Basic RegEx 簡(jiǎn)稱 BREs )

擴(kuò)展的正則表達(dá)式( Extended Regular Expression 又叫 Extended RegEx 簡(jiǎn)稱 EREs )

Perl 的正則表達(dá)式( Perl Regular Expression 又叫 Perl RegEx 簡(jiǎn)稱 PREs )

正則表達(dá)式比較
字符 說明 Basic RegEx Extended RegEx python RegEx Perl regEx
轉(zhuǎn)義
^ 匹配行首,例如"^dog"匹配以字符串dog開頭的行(注意:awk 指令中,"^"則是匹配字符串的開始) ^ ^ ^ ^
$ 匹配行尾,例如:"^、dog$" 匹配以字符串 dog 為結(jié)尾的行(注意:awk 指令中,"$"則是匹配字符串的結(jié)尾) $ $ $ $
^$ 匹配空行 ^$ ^$ ^$ ^$
^string$ 匹配行,例如:"^dog$"匹配只含一個(gè)字符串 dog 的行 ^string$ ^string$ ^string$ ^string$
< 匹配單詞,例如:" < < 不支持 不支持(但可以使用b來匹配單詞,例如:"bfrog")
> 匹配單詞,例如:"frog>"(等價(jià)于"frogb "),匹配以 frog 結(jié)尾的單詞 > > 不支持 不支持(但可以使用b來匹配單詞,例如:"frogb")
匹配一個(gè)單詞或者一個(gè)特定字符,例如:""(等價(jià)于"bfrogb")、"" 不支持 不支持(但可以使用b來匹配單詞,例如:"bfrogb"
() 匹配表達(dá)式,例如:不支持"(frog)" 不支持(但可以使用,如:dog () () ()
匹配表達(dá)式,例如:不支持"(frog)" 不支持(同()) 不支持(同()) 不支持(同())
匹配前面的子表達(dá)式 0 次或 1 次(等價(jià)于{0,1}),例如:where(is)?能匹配"where" 以及"whereis" 不支持(同?) ? ? ?
? 匹配前面的子表達(dá)式 0 次或 1 次(等價(jià)于"{0,1}"),例如:"whereis? "能匹配 "where"以及"whereis" ? 不支持(同?) 不支持(同?) 不支持(同?)
? 當(dāng)該字符緊跟在任何一個(gè)其他限制符(*, +, ?, {n},{n,}, {n,m}) 后面時(shí),匹配模式是非貪婪的。非貪婪模式盡可能少的匹配所搜索的字符串,而默認(rèn)的貪婪模式則盡可能多的匹配所搜索的字符串。例如,對(duì)于字符串 "oooo","o+?" 將匹配單個(gè)"o",而 "o+" 將匹配所有 "o" 不支持 不支持 不支持 不支持
. 匹配除換行符("n")之外的任意單個(gè)字符(注意:awk 指令中的句點(diǎn)能匹配換行符) . .(如果要匹配包括“n”在內(nèi)的任何一個(gè)字符,請(qǐng)使用: [sS] . .(如果要匹配包括“n”在內(nèi)的任何一個(gè)字符,請(qǐng)使用:" [.n] "
* 匹配前面的子表達(dá)式 0 次或多次(等價(jià)于{0, }),例如:zo* 能匹配 "z"以及 "zoo" * * * *
+ 匹配前面的子表達(dá)式 1 次或多次(等價(jià)于"{1, }"),例如:"whereis+ "能匹配 "whereis"以及"whereisis" + 不支持(同+) 不支持(同+) 不支持(同+)
+ 匹配前面的子表達(dá)式 1 次或多次(等價(jià)于{1, }),例如:zo+能匹配 "zo"以及 "zoo",但不能匹配 "z" 不支持(同+) + + +
{n} n 必須是一個(gè) 0 或者正整數(shù),匹配子表達(dá)式 n 次,例如:zo{2}能匹配 不支持(同{n}) {n} {n} {n}
{n,} "zooz",但不能匹配 "Bob"n 必須是一個(gè) 0 或者正整數(shù),匹配子表達(dá)式大于等于 n次,例如:go{2,} 不支持(同{n,}) {n,} {n,} {n,}
{n,m} 能匹配 "good",但不能匹配 godm 和 n 均為非負(fù)整數(shù),其中 n <= m,最少匹配 n 次且最多匹配 m 次 ,例如:o{1,3}將配"fooooood" 中的前三個(gè) o(請(qǐng)注意在逗號(hào)和兩個(gè)數(shù)之間不能有空格) 不支持(同{n,m}) {n,m} {n,m} {n,m}
x l y 匹配 x 或 y 不支持(同x l y x l y x l y x l y
[0-9] 匹配從 0 到 9 中的任意一個(gè)數(shù)字字符(注意:要寫成遞增) [0-9] [0-9] [0-9] [0-9]
[xyz] 字符集合,匹配所包含的任意一個(gè)字符,例如:"[abc]"可以匹配"lay" 中的 "a"(注意:如果元字符,例如:. *等,它們被放在[ ]中,那么它們將變成一個(gè)普通字符) [xyz] [xyz] [xyz] [xyz]
[^xyz] 負(fù)值字符集合,匹配未包含的任意一個(gè)字符(注意:不包括換行符),例如:"[^abc]" 可以匹配 "Lay" 中的"L"(注意:[^xyz]在awk 指令中則是匹配未包含的任意一個(gè)字符+換行符) [^xyz] [^xyz] [^xyz] [^xyz]
[A-Za-z] 匹配大寫字母或者小寫字母中的任意一個(gè)字符(注意:要寫成遞增) [A-Za-z] [A-Za-z] [A-Za-z] [A-Za-z]
[^A-Za-z] 匹配除了大寫與小寫字母之外的任意一個(gè)字符(注意:寫成遞增) [^A-Za-z] [^A-Za-z] [^A-Za-z] [^A-Za-z]
d 匹配從 0 到 9 中的任意一個(gè)數(shù)字字符(等價(jià)于 [0-9]) 不支持 不支持 d d
D 匹配非數(shù)字字符(等價(jià)于 1 不支持 不支持 D D
S 匹配任何非空白字符(等價(jià)于2 不支持 不支持 S S
s 匹配任何空白字符,包括空格、制表符、換頁(yè)符等等(等價(jià)于[ fnrtv]) 不支持 不支持 s s
W 匹配任何非單詞字符 (等價(jià)于3) W W W W
w 匹配包括下劃線的任何單詞字符(等價(jià)于[A-Za-z0-9_]) w w w w
B 匹配非單詞邊界,例如:"erB" 能匹配 "verb" 中的"er",但不能匹配"never" 中的"er" B B B B
 匹配一個(gè)單詞邊界,也就是指單詞和空格間的位置,例如: "erb" 可以匹配"never" 中的 "er",但不能匹配 "verb" 中的"er"    
匹配一個(gè)橫向制表符(等價(jià)于 x09和 cI) 不支持 不支持
v 匹配一個(gè)垂直制表符(等價(jià)于 x0b和 cK) 不支持 不支持 v v
匹配一個(gè)換行符(等價(jià)于 x0a 和cJ) 不支持 不支持
f 匹配一個(gè)換頁(yè)符(等價(jià)于x0c 和cL) 不支持 不支持 f f
匹配一個(gè)回車符(等價(jià)于 x0d 和cM) 不支持 不支持
匹配轉(zhuǎn)義字符本身""
cx 匹配由 x 指明的控制字符,例如:cM匹配一個(gè)Control-M 或回車符,x 的值必須為A-Z 或 a-z 之一,否則,將 c 視為一個(gè)原義的 "c" 字符 不支持 不支持 cx
xn 匹配 n,其中 n 為十六進(jìn)制轉(zhuǎn)義值。十六進(jìn)制轉(zhuǎn)義值必須為確定的兩個(gè)數(shù)字長(zhǎng),例如:"x41" 匹配 "A"。"x041" 則等價(jià)于"x04" & "1"。正則表達(dá)式中可以使用 ASCII 編碼 不支持 不支持 xn
num 匹配 num,其中 num是一個(gè)正整數(shù)。表示對(duì)所獲取的匹配的引用 不支持 num num
[:alnum:] 匹配任何一個(gè)字母或數(shù)字([A-Za-z0-9]),例如:"[[:alnum:]] " [:alnum:] [:alnum:] [:alnum:] [:alnum:]
[:alpha:] 匹配任何一個(gè)字母([A-Za-z]), 例如:" [[:alpha:]] " [:alpha:] [:alpha:] [:alpha:] [:alpha:]
[:digit:] 匹配任何一個(gè)數(shù)字([0-9]),例如:"[[:digit:]] " [:digit:] [:digit:] [:digit:] [:digit:]
[:lower:] 匹配任何一個(gè)小寫字母([a-z]), 例如:" [[:lower:]] " [:lower:] [:lower:] [:lower:] [:lower:]
[:upper:] 匹配任何一個(gè)大寫字母([A-Z]) [:upper:] [:upper:] [:upper:] [:upper:]
[:space:] 任何一個(gè)空白字符: 支持制表符、空格,例如:" [[:space:]] " [:space:] [:space:] [:space:] [:space:]
[:blank:] 空格和制表符(橫向和縱向),例如:"[[:blank:]]"ó"[stv]" [:blank:] [:blank:] [:blank:] [:blank:]
[:graph:] 任何一個(gè)可以看得見的且可以打印的字符(注意:不包括空格和換行符等),例如:"[[:graph:]] " [:graph:] [:graph:] [:graph:] [:graph:]
[:print:] 任何一個(gè)可以打印的字符(注意:不包括:[:cntrl:]、字符串結(jié)束符"0"、EOF 文件結(jié)束符(-1), 但包括空格符號(hào)),例如:"[[:print:]] " [:print:] [:print:] [:print:] [:print:]
[:cntrl:] 任何一個(gè)控制字符(ASCII 字符集中的前 32 個(gè)字符,即:用十進(jìn)制表示為從 0 到31,例如:換行符、制表符等等),例如:" [[:cntrl:]]" [:cntrl:] [:cntrl:] [:cntrl:] [:cntrl:]
[:punct:] 任何一個(gè)標(biāo)點(diǎn)符號(hào)(不包括:[:alnum:]、[:cntrl:]、[:space:]這些字符集) [:punct:] [:punct:] [:punct:] [:punct:]
[:xdigit:] 任何一個(gè)十六進(jìn)制數(shù)(即:0-9,a-f,A-F) [:xdigit:] [:xdigit:] [:xdigit:] [:xdigit:]

注意

js中支持的是EREs.

當(dāng)使用 BREs ( 基本正則表達(dá)式 ) 時(shí),必須在下列這些符號(hào)(?,+,|,{,},(,))前加上轉(zhuǎn)義字符 .

上述[[:xxxx:]] 形式的正則表達(dá)式, 是php中內(nèi)置的通用字符簇, js中并不支持.

linux/osx下常用命令與正則表達(dá)式的關(guān)系

我曾經(jīng)嘗試在 grep 和 sed 命令中書寫正則表達(dá)式, 經(jīng)常發(fā)現(xiàn)不能使用元字符, 而且有時(shí)候需要轉(zhuǎn)義, 有時(shí)候不需要轉(zhuǎn)義, 始終不能摸清它的規(guī)律. 如果恰好你也有同樣的困惑, 那么請(qǐng)往下看, 相信應(yīng)該能有所收獲.

grep , egrep , sed , awk 正則表達(dá)式特點(diǎn)

grep 支持:BREs、EREs、PREs 正則表達(dá)式

grep 指令后不跟任何參數(shù), 則表示要使用 "BREs"

grep 指令后跟 ”-E" 參數(shù), 則表示要使用 "EREs"

grep 指令后跟 “-P" 參數(shù), 則表示要使用 "PREs"

egrep 支持:EREs、PREs 正則表達(dá)式

egrep 指令后不跟任何參數(shù), 則表示要使用 "EREs"

egrep 指令后跟 “-P" 參數(shù), 則表示要使用 "PREs"

sed 支持: BREs、EREs

sed 指令默認(rèn)是使用 "BREs"

sed 指令后跟 "-r" 參數(shù) , 則表示要使用“EREs"

awk 支持 EREs, 并且默認(rèn)使用 "EREs"

正則表達(dá)式初階技能 貪婪模式與非貪婪模式

默認(rèn)情況下, 所有的限定詞都是貪婪模式, 表示盡可能多的去捕獲字符; 而在限定詞后增加?, 則是非貪婪模式, 表示盡可能少的去捕獲字符. 如下:

var str = "aaab",
    reg1 = /a+/, //貪婪模式
    reg2 = /a+?/;//非貪婪模式
console.log(str.match(reg1)); //["aaa"], 由于是貪婪模式, 捕獲了所有的a
console.log(str.match(reg2)); //["a"], 由于是非貪婪模式, 只捕獲到第一個(gè)a

實(shí)際上, 非貪婪模式非常有效, 特別是當(dāng)匹配html標(biāo)簽時(shí). 比如匹配一個(gè)配對(duì)出現(xiàn)的div, 方案一可能會(huì)匹配到很多的div標(biāo)簽對(duì), 而方案二則只會(huì)匹配一個(gè)div標(biāo)簽對(duì).

var str = "
test
"; var reg1 = //; //方案一,貪婪匹配 var reg2 = //;//方案二,非貪婪匹配 console.log(str.match(reg1));//"
test
" console.log(str.match(reg2));//"
test
"
區(qū)間量詞的非貪婪模式

一般情況下, 非貪婪模式, 我們使用的是"*?", 或 "+?" 這種形式, 還有一種是 "{n,m}?".

區(qū)間量詞"{n,m}" 也是匹配優(yōu)先, 雖有匹配次數(shù)上限, 但是在到達(dá)上限之前, 它依然是盡可能多的匹配, 而"{n,m}?" 則表示在區(qū)間范圍內(nèi), 盡可能少的匹配.

需要注意的是:

能達(dá)到同樣匹配結(jié)果的貪婪與非貪婪模式, 通常是貪婪模式的匹配效率較高.

所有的非貪婪模式, 都可以通過修改量詞修飾的子表達(dá)式, 轉(zhuǎn)換為貪婪模式.

貪婪模式可以與固化分組(后面會(huì)講到)結(jié)合,提升匹配效率,而非貪婪模式卻不可以.

分組

正則的分組主要通過小括號(hào)來實(shí)現(xiàn), 括號(hào)包裹的子表達(dá)式作為一個(gè)分組, 括號(hào)后可以緊跟限定詞表示重復(fù)次數(shù). 如下, 小括號(hào)內(nèi)包裹的abc便是一個(gè)分組:

/(abc)+/.test("abc123") == true

那么分組有什么用呢? 一般來說, 分組是為了方便的表示重復(fù)次數(shù), 除此之外, 還有一個(gè)作用就是用于捕獲, 請(qǐng)往下看.

捕獲性分組

捕獲性分組, 通常由一對(duì)小括號(hào)加上子表達(dá)式組成. 捕獲性分組會(huì)創(chuàng)建反向引用, 每個(gè)反向引用都由一個(gè)編號(hào)或名稱來標(biāo)識(shí), js中主要是通過 $+編號(hào) 或者 +編號(hào) 表示法進(jìn)行引用. 如下便是一個(gè)捕獲性分組的例子.

var color = "#808080";
var output = color.replace(/#(d+)/,"$1"+"~~");//自然也可以寫成 "$1~~"
console.log(RegExp.$1);//808080
console.log(output);//808080~~

以上, (d+) 表示一個(gè)捕獲性分組, "RegExp.$1" 指向該分組捕獲的內(nèi)容. $+編號(hào) 這種引用通常在正則表達(dá)式之外使用. +編號(hào) 這種引用卻可以在正則表達(dá)式中使用, 可用于匹配不同位置相同部分的子串.

var url = "www.google.google.com";
var re = /([a-z]+).1/;
console.log(url.replace(re,"$1"));//"www.google.com"

以上, 相同部分的"google"字符串只被替換一次.

非捕獲性分組

非捕獲性分組, 通常由一對(duì)括號(hào)加上"?:"加上子表達(dá)式組成, 非捕獲性分組不會(huì)創(chuàng)建反向引用, 就好像沒有括號(hào)一樣. 如下:

var color = "#808080";
var output = color.replace(/#(?:d+)/,"$1"+"~~");
console.log(RegExp.$1);//""
console.log(output);//$1~~

以上, (?:d+) 表示一個(gè)非捕獲性分組, 由于分組不捕獲任何內(nèi)容, 所以, RegExp.$1 就指向了空字符串.
同時(shí), 由于$1 的反向引用不存在, 因此最終它被當(dāng)成了普通字符串進(jìn)行替換.
實(shí)際上, 捕獲性分組和無捕獲性分組在搜索效率方面也沒什么不同, 沒有哪一個(gè)比另一個(gè)更快.

命名分組

語法: (?...)

命名分組也是捕獲性分組, 它將匹配的字符串捕獲到一個(gè)組名稱或編號(hào)名稱中, 在獲得匹配結(jié)果后, 可通過分組名進(jìn)行獲取. 如下是一個(gè)python的命名分組的例子.

import re
data = "#808080"
regExp = r"#(?Pd+)"
replaceString = "g" + "~~"
print re.sub(regExp,replaceString,data) # 808080~~

python的命名分組表達(dá)式與標(biāo)準(zhǔn)格式相比, 在 ? 后多了一大寫的 P 字符, 并且python通過“g<命名>"表示法進(jìn)行引用. (如果是捕獲性分組, python通過"g<編號(hào)>"表示法進(jìn)行引用)

與python不同的是, javaScript 中并不支持命名分組.

固化分組

固化分組, 又叫原子組.

語法: (?>...)

如上所述, 我們?cè)谑褂梅秦澙纺J綍r(shí), 匹配過程中可能會(huì)進(jìn)行多次的回溯, 回溯越多, 正則表達(dá)式的運(yùn)行效率就越低. 而固化分組就是用來減少回溯次數(shù)的.

實(shí)際上, 固化分組(?>…)的匹配與正常的匹配并無分別, 它并不會(huì)改變匹配結(jié)果. 唯一的不同就是: 固化分組匹配結(jié)束時(shí), 它匹配到的文本已經(jīng)固化為一個(gè)單元, 只能作為整體而保留或放棄, 括號(hào)內(nèi)的子表達(dá)式中未嘗試過的備用狀態(tài)都會(huì)被放棄, 所以回溯永遠(yuǎn)也不能選擇其中的狀態(tài)(因此不能參與回溯). 下面我們來通過一個(gè)例子更好地理解固化分組.

假如要處理一批數(shù)據(jù), 原格式為 123.456, 因?yàn)楦↑c(diǎn)數(shù)顯示問題, 部分?jǐn)?shù)據(jù)格式會(huì)變?yōu)?23.456000000789這種, 現(xiàn)要求只保留小數(shù)點(diǎn)后2~3位, 但是最后一位不能為0, 那么這個(gè)正則怎么寫呢?

var str = "123.456000000789";
str = str.replace(/(.dd[1-9]?)d*/,"$1"); //123.456

以上的正則, 對(duì)于"123.456" 這種格式的數(shù)據(jù), 將白白處理一遍. 為了提高效率, 我們將正則最后的一個(gè)"*"改為"+". 如下:

var str = "123.456";
str = str.replace(/(.dd[1-9]?)d+/,"$1"); //123.45

此時(shí), "dd[1-9]?" 子表達(dá)式, 匹配是 "45", 而不是 "456", 這是因?yàn)檎齽t末尾使用了"+", 表示末尾至少要匹配一個(gè)數(shù)字, 因此末尾的子表達(dá)式"d+" 匹配到了 "6". 顯然 "123.45" 不是我們期望的匹配結(jié)果, 那我們應(yīng)該怎么做呢? 能否讓 "[1-9]?" 一旦匹配成功, 便不再進(jìn)行回溯, 這里就要用到我們上面說的固化分組.

"(.dd(?>[1-9]?))d+" 便是上述正則的固化分組形式. 由于字符串 "123.456" 不滿足該固化分組的正則, 所以, 匹配會(huì)失敗, 符合我們期望.

下面我們來分析下固化分組的正則 (.dd(?>[1-9]?))d+ 為什么匹配不到字符串"123.456".

很明顯, 對(duì)于上述固化分組, 只存在兩種匹配結(jié)果.

情況①: 若 [1-9] 匹配失敗, 正則會(huì)返回 ? 留下的備用狀態(tài). 然后匹配脫離固化分組, 繼續(xù)前進(jìn)到[d+]. 當(dāng)控制權(quán)離開固化分組時(shí), 沒有備用狀態(tài)需要放棄(因固化分組中根本沒有創(chuàng)建任何備用狀態(tài)).

情況②: 若 [1-9] 匹配成功, 匹配脫離固化分組之后, ? 保存的備用狀態(tài)仍然存在, 但是, 由于它屬于已經(jīng)結(jié)束的固化分組, 所以會(huì)被拋棄.

對(duì)于字符串 "123.456", 由于 [1-9] 能夠匹配成功, 所以它符合情況②. 下面我們來還原情況②的執(zhí)行現(xiàn)場(chǎng).

匹配所處的狀態(tài): 匹配已經(jīng)走到了 "6" 的位置, 匹配將繼續(xù)前進(jìn);==>

子表達(dá)式 d+ 發(fā)現(xiàn)無法匹配, 正則引擎便嘗試回溯;==>

查看是否存在備用狀態(tài)以供回溯?==>

"?" 保存的備用狀態(tài)屬于已經(jīng)結(jié)束的固化分組, 所以該備用狀態(tài)會(huì)被放棄;==>

此時(shí)固化分組匹配到的 "6", 便不能用于正則引擎的回溯;==>

嘗試回溯失敗;==>

正則匹配失敗.==>

文本 "123.456" 沒有被正則表達(dá)式匹配上, 符合預(yù)期.

相應(yīng)的流程圖如下:

遺憾的是, javaScript, java 和 python中并不支持固化分組的語法, 不過, 它在php和.NET中表現(xiàn)良好. 下面提供了一個(gè)php版的固化分組形式的正則表達(dá)式, 以供嘗試.

$str = "123.456";
echo preg_replace("/(.dd(?>[1-9]?))d+/","1",$str); //固化分組

不僅如此, php還提供了占有量詞優(yōu)先的語法. 如下:

$str = "123.456";
echo preg_replace("/(.dd[1-9]?+)d+/","1",$str); //占有量詞優(yōu)先

雖然java不支持固化分組的語法, 但java也提供了占有量詞優(yōu)先的語法, 同樣能夠避免正則回溯. 如下:

String str = "123.456";
System.out.println(str.replaceAll("(.dd[1-9]?+)d+", "$1"));// 123.456

值得注意的是: java中 replaceAll 方法需要轉(zhuǎn)義反斜杠.

正則表達(dá)式高階技能-零寬斷言

如果說正則分組是寫輪眼, 那么零寬斷言就是萬花筒寫輪眼終極奧義-須佐能乎(這里借火影忍術(shù)打個(gè)比方). 合理地使用零寬斷言, 能夠能分組之不能, 極大地增強(qiáng)正則匹配能力, 它甚至可以幫助你在匹配條件非常模糊的情況下快速地定位文本.

零寬斷言, 又叫環(huán)視. 環(huán)視只進(jìn)行子表達(dá)式的匹配, 匹配到的內(nèi)容不保存到最終的匹配結(jié)果, 由于匹配是零寬度的, 故最終匹配到的只是一個(gè)位置.

環(huán)視按照方向劃分, 有順序和逆序兩種(也叫前瞻和后瞻), 按照是否匹配有肯定和否定兩種, 組合之, 便有4種環(huán)視. 4種環(huán)視并不復(fù)雜, 如下便是它們的描述.

字符 描述 示例
(?:pattern) 非捕獲性分組, 匹配pattern的位置, 但不捕獲匹配結(jié)果.也就是說不創(chuàng)建反向引用, 就好像沒有括號(hào)一樣. "abcd(?:e)匹配"abcde
(?=pattern) 順序肯定環(huán)視, 匹配后面是pattern 的位置, 不捕獲匹配結(jié)果. "Windows (?=2000)"匹配 "Windows2000" 中的 "Windows"; 不匹配 "Windows3.1" 中的 "Windows"
(?!pattern) 順序否定環(huán)視, 匹配后面不是 pattern 的位置, 不捕獲匹配結(jié)果. "Windows (?!2000)"匹配 "Windows3.1" 中的 "Windows"; 不匹配 "Windows2000" 中的 "Windows"
(?<=pattern) 逆序肯定環(huán)視, 匹配前面是 pattern 的位置, 不捕獲匹配結(jié)果. "(?<=Office)2000"匹配 " Office2000" 中的 "2000"; 不匹配 "Windows2000" 中的 "2000"
(?pattern) 逆序否定環(huán)視, 匹配前面不是 pattern 的位置, 不捕獲匹配結(jié)果. "(?

非捕獲性分組由于結(jié)構(gòu)與環(huán)視相似, 故列在表中, 以做對(duì)比. 以上4種環(huán)視中, 目前 javaScript 中只支持前兩種, 也就是只支持 順序肯定環(huán)視順序否定環(huán)視. 下面我們通過實(shí)例來幫助理解下:

var str = "123abc789",s;
//沒有使用環(huán)視,abc直接被替換
s = str.replace(/abc/,456);
console.log(s); //123456789

//使用了順序肯定環(huán)視,捕獲到了a前面的位置,所以abc沒有被替換,只是將3替換成了3456
s = str.replace(/3(?=abc)/,3456);
console.log(s); //123456abc789

//使用了順序否定環(huán)視,由于3后面跟著abc,不滿意條件,故捕獲失敗,所以原字符串沒有被替換
s = str.replace(/3(?!abc)/,3456);
console.log(s); //123abc789

下面通過python來演示下 逆序肯定環(huán)視逆序否定環(huán)視 的用法.

import re
data = "123abc789"
# 使用了逆序肯定環(huán)視,替換左邊為123的連續(xù)的小寫英文字母,匹配成功,故abc被替換為456
regExp = r"(?<=123)[a-z]+"
replaceString = "456"
print re.sub(regExp,replaceString,data) # 123456789

# 使用了逆序否定環(huán)視,由于英文字母左側(cè)不能為123,故子表達(dá)式[a-z]+捕獲到bc,最終bc被替換為456
regExp = r"(?

需要注意的是: python 和 perl 語言中的 逆序環(huán)視 的子表達(dá)式只能使用定長(zhǎng)的文本. 比如將上述 "(?<=123)" (逆序肯定環(huán)視)子表達(dá)式寫成 "(?<=[0-9]+)", python解釋器將會(huì)報(bào)錯(cuò): "error: look-behind requires fixed-width pattern".

場(chǎng)景回顧 獲取html片段

假如現(xiàn)在, js 通過 ajax 獲取到一段 html 代碼如下:

var responseText = "
";

現(xiàn)我們需要替換img標(biāo)簽的src 屬性中的 "dev"字符串 為 "test" 字符串.

① 由于上述 responseText 字符串中包含至少兩個(gè)子字符串 "dev", 顯然不能直接 replace 字符串 "dev"為 "test".

② 同時(shí)由于 js 中不支持逆序環(huán)視, 我們也不能在正則中判斷前綴為 "src="", 然后再替換"dev".

③ 我們注意到 img 標(biāo)簽的 src 屬性以 ".png" 結(jié)尾, 基于此, 就可以使用順序肯定環(huán)視. 如下:

var reg = /dev(?=[^"]*png)/; //為了防止匹配到第一個(gè)dev, 通配符前面需要排除單引號(hào)或者是尖括號(hào)
var str = responseText.replace(reg,"test");
console.log(str);//

當(dāng)然, 以上不止順序肯定環(huán)視一種解法, 捕獲性分組同樣可以做到. 那么環(huán)視高級(jí)在哪里呢? 環(huán)視高級(jí)的地方就在于它通過一次捕獲就可以定位到一個(gè)位置, 對(duì)于復(fù)雜的文本替換場(chǎng)景, 常有奇效, 而分組則需要更多的操作.

千位分割符

千位分隔符, 顧名思義, 就是數(shù)字中的逗號(hào). 參考西方的習(xí)慣, 數(shù)字之中加入一個(gè)符號(hào), 避免因數(shù)字太長(zhǎng)難以直觀的看出它的值. 故而數(shù)字之中, 每隔三位添加一個(gè)逗號(hào), 即千位分隔符.

那么怎么將一串?dāng)?shù)字轉(zhuǎn)化為千位分隔符形式呢?

var str = "1234567890";
(+str).toLocaleString();//"1,234,567,890"

如上, toLocaleString() 返回當(dāng)前對(duì)象的"本地化"字符串形式.

如果該對(duì)象是Number類型, 那么將返回該數(shù)值的按照特定符號(hào)分割的字符串形式.

如果該對(duì)象是Array類型, 那么先將數(shù)組中的每項(xiàng)轉(zhuǎn)化為字符串, 然后將這些字符串以指定分隔符連接起來并返回.

toLocaleString 方法特殊, 有本地化特性, 對(duì)于天朝, 默認(rèn)的分隔符是英文逗號(hào). 因此使用它恰好可以將數(shù)值轉(zhuǎn)化為千位分隔符形式的字符串. 如果考慮到國(guó)際化, 以上方法就有可能會(huì)失效了.

我們嘗試使用環(huán)視來處理下.

function thousand(str){
  return str.replace(/(?!^)(?=([0-9]{3})+$)/g,",");
}
console.log(thousand(str));//"1,234,567,890"
console.log(thousand("123456"));//"123,456"
console.log(thousand("1234567879876543210"));//"1,234,567,879,876,543,210"

上述使用到的正則分為兩塊. (?!^)(?=([0-9]{3})+$). 我們先來看后面的部分, 然后逐步分析之.

"[0-9]{3}" 表示連續(xù)3位數(shù)字.

"([0-9]{3})+" 表示連續(xù)3位數(shù)字至少出現(xiàn)一次或更多次.

"([0-9]{3})+$" 表示連續(xù)3的正整數(shù)倍的數(shù)字, 直到字符串末尾.

那么 (?=([0-9]{3})+$) 就表示匹配一個(gè)零寬度的位置, 并且從這個(gè)位置到字符串末尾, 中間擁有3的正整數(shù)倍的數(shù)字.

正則表達(dá)式使用全局匹配g, 表示匹配到一個(gè)位置后, 它會(huì)繼續(xù)匹配, 直至匹配不到.

將這個(gè)位置替換為逗號(hào), 實(shí)際上就是每3位數(shù)字添加一個(gè)逗號(hào).

當(dāng)然對(duì)于字符串"123456"這種剛好擁有3的正整數(shù)倍的數(shù)字的, 當(dāng)然不能在1前面添加逗號(hào). 那么使用 (?!^) 就指定了這個(gè)替換的位置不能為起始位置.

千位分隔符實(shí)例, 展示了環(huán)視的強(qiáng)大, 一步到位.

正則表達(dá)式在JS中的應(yīng)用 ES6對(duì)正則的擴(kuò)展

ES6對(duì)正則擴(kuò)展了又兩種修飾符(其他語言可能不支持):

y (粘連sticky修飾符), 與g類似, 也是全局匹配, 并且下一次匹配都是從上一次匹配成功的下一個(gè)位置開始, 不同之處在于, g修飾符只要剩余位置中存在匹配即可, 而y修飾符確保匹配必須從剩余的第一個(gè)位置開始.

var s = "abc_ab_a";
var r1 = /[a-z]+/g;
var r2 = /[a-z]+/y;
console.log(r1.exec(s),r1.lastIndex); // ["abc", index: 0, input: "abc_ab_a"] 3
console.log(r2.exec(s),r2.lastIndex); // ["abc", index: 0, input: "abc_ab_a"] 3

console.log(r1.exec(s),r1.lastIndex); // ["ab", index: 4, input: "abc_ab_a"] 6
console.log(r2.exec(s),r2.lastIndex); // null 0

如上, 由于第二次匹配的開始位置是下標(biāo)3, 對(duì)應(yīng)的字符串是 "_", 而使用y修飾符的正則對(duì)象r2, 需要從剩余的第一個(gè)位置開始, 所以匹配失敗, 返回null.

正則對(duì)象的 sticky 屬性, 表示是否設(shè)置了y修飾符. 這點(diǎn)將會(huì)在后面講到.

u 修飾符, 提供了對(duì)正則表達(dá)式添加4字節(jié)碼點(diǎn)的支持. 比如 "?" 字符是一個(gè)4字節(jié)字符, 直接使用正則匹配將會(huì)失敗, 而使用u修飾符后, 將會(huì)等到正確的結(jié)果.

var s = "?";
console.log(/^.$/.test(s));//false
console.log(/^.$/u.test(s));//true
UCS-2字節(jié)碼

有關(guān)字節(jié)碼點(diǎn), 稍微提下. javaScript 只能處理UCS-2編碼(js于1995年5月被Brendan Eich花費(fèi)10天設(shè)計(jì)出來, 比1996年7月發(fā)布的編碼規(guī)范UTF-16早了一年多, 當(dāng)時(shí)只有UCS-2可選). 由于UCS-2先天不足, 造成了所有字符在js中都是2個(gè)字節(jié). 如果是4個(gè)字節(jié)的字符, 將會(huì)默認(rèn)被當(dāng)作兩個(gè)雙字節(jié)字符處理. 因此 js 的字符處理函數(shù)都會(huì)受到限制, 無法返回正確結(jié)果. 如下:

var s = "?";
console.log(s == "uD834uDF06");//true ?相當(dāng)于UTF-16中的0xD834DF06
console.log(s.length);//2 長(zhǎng)度為2, 表示這是4字節(jié)字符

幸運(yùn)的是, ES6可以自動(dòng)識(shí)別4字節(jié)的字符.因此遍歷字符串可以直接使用for of循環(huán). 同時(shí), js中如果直接使用碼點(diǎn)表示Unicode字符, 對(duì)于4字節(jié)字符, ES5里是沒辦法識(shí)別的. 為此ES6修復(fù)了這個(gè)問題, 只需將碼點(diǎn)放在大括號(hào)內(nèi)即可.

console.log(s === "u1D306");//false   ES5無法識(shí)別?
console.log(s === "u{1D306}");//true  ES6可以借助大括號(hào)識(shí)別?
附: ES6新增的處理4字節(jié)碼的函數(shù)

String.fromCodePoint():從Unicode碼點(diǎn)返回對(duì)應(yīng)字符

String.prototype.codePointAt():從字符返回對(duì)應(yīng)的碼點(diǎn)

String.prototype.at():返回字符串給定位置的字符

有關(guān)js中的unicode字符集, 請(qǐng)參考阮一峰老師的 Unicode與JavaScript詳解 .

以上是ES6對(duì)正則的擴(kuò)展. 另一個(gè)方面, 從方法上看, javaScript 中與正則表達(dá)式有關(guān)的方法有:

方法名 compile test exec match search replace split
所屬對(duì)象 RegExp RegExp RegExp String String String String

由上, 一共有7個(gè)與js相關(guān)的方法, 這些方法分別來自于 RegExp 與 String 對(duì)象. 首先我們先來看看js中的正則類 RegExp.

RegExp

RegExp 對(duì)象表示正則表達(dá)式, 主要用于對(duì)字符串執(zhí)行模式匹配.

語法: new RegExp(pattern[, flags])

參數(shù) pattern 是一個(gè)字符串, 指定了正則表達(dá)式字符串或其他的正則表達(dá)式對(duì)象.

參數(shù) flags 是一個(gè)可選的字符串, 包含屬性 "g"、"i" 和 "m", 分別用于指定全局匹配、區(qū)分大小寫的匹配和多行匹配. 如果pattern 是正則表達(dá)式, 而不是字符串, 則必須省略該參數(shù).

var pattern = "[0-9]";
var reg = new RegExp(pattern,"g");
// 上述創(chuàng)建正則表達(dá)式對(duì)象,可以用對(duì)象字面量形式代替,也推薦下面這種
var reg = /[0-9]/g;

以上, 通過對(duì)象字面量和構(gòu)造函數(shù)創(chuàng)建正則表達(dá)式, 有個(gè)小插曲.

"對(duì)于正則表達(dá)式的直接量, ECMAscript 3規(guī)定在每次它時(shí)都會(huì)返回同一個(gè)RegExp對(duì)象, 因此用直接量創(chuàng)建的正則表達(dá)式的會(huì)共享一個(gè)實(shí)例. 直到ECMAScript 5才規(guī)定每次返回不同的實(shí)例."

所以, 現(xiàn)在我們基本不用擔(dān)心這個(gè)問題, 只需要注意在低版本的非IE瀏覽器中盡量使用構(gòu)造函數(shù)創(chuàng)建正則(這點(diǎn)上, IE一直遵守ES5規(guī)定, 其他瀏覽器的低級(jí)版本遵循ES3規(guī)定).

RegExp 實(shí)例對(duì)象包含如下屬性:

實(shí)例屬性 描述
global 是否包含全局標(biāo)志(true/false)
ignoreCase 是否包含區(qū)分大小寫標(biāo)志(true/false)
multiline 是否包含多行標(biāo)志(true/false)
source 返回創(chuàng)建RegExp對(duì)象實(shí)例時(shí)指定的表達(dá)式文本字符串形式
lastIndex 表示原字符串中匹配的字符串末尾的后一個(gè)位置, 默認(rèn)為0
flags(ES6) 返回正則表達(dá)式的修飾符
sticky(ES6) 是否設(shè)置了y(粘連)修飾符(true/false)
compile

compile 方法用于在執(zhí)行過程中改變和重新編譯正則表達(dá)式.

語法: compile(pattern[, flags])

參數(shù)介紹請(qǐng)參考上述 RegExp 構(gòu)造器. 用法如下:

var reg = new RegExp("abc", "gi"); 
var reg2 = reg.compile("new abc", "g");
console.log(reg);// /new abc/g
console.log(reg2);// undefined

可見 compile 方法會(huì)改變?cè)齽t表達(dá)式對(duì)象, 并重新編譯, 而且它的返回值為空.

test

test 方法用于檢測(cè)一個(gè)字符串是否匹配某個(gè)正則規(guī)則, 只要是字符串中含有與正則規(guī)則匹配的文本, 該方法就返回true, 否則返回 false.

語法: test(string), 用法如下:

console.log(/[0-9]+/.test("abc123"));//true
console.log(/[0-9]+/.test("abc"));//false

以上, 字符串"abc123" 包含數(shù)字, 故 test 方法返回 true; 而 字符串"abc" 不包含數(shù)字, 故返回 false.

如果需要使用 test 方法測(cè)試字符串是否完成匹配某個(gè)正則規(guī)則, 那么可以在正則表達(dá)式里增加開始(^)和結(jié)束($)元字符. 如下:

console.log(/^[0-9]+$/.test("abc123"));//false

以上, 由于字符串"abc123" 并非以數(shù)字開始, 也并非以數(shù)字結(jié)束, 故 test 方法返回false.

實(shí)際上, 如果正則表達(dá)式帶有全局標(biāo)志(帶有參數(shù)g)時(shí), test 方法還受正則對(duì)象的lastIndex屬性影響,如下:

var reg = /[a-z]+/;//正則不帶全局標(biāo)志
console.log(reg.test("abc"));//true
console.log(reg.test("de"));//true

var reg = /[a-z]+/g;//正則帶有全局標(biāo)志g
console.log(reg.test("abc"));//true
console.log(reg.lastIndex);//3, 下次運(yùn)行test時(shí),將從索引為3的位置開始查找
console.log(reg.test("de"));//false

該影響將在exec 方法講解中予以分析.

exec

exec 方法用于檢測(cè)字符串對(duì)正則表達(dá)式的匹配, 如果找到了匹配的文本, 則返回一個(gè)結(jié)果數(shù)組, 否則返回null.

語法: exec(string)

exec 方法返回的數(shù)組中包含兩個(gè)額外的屬性, index 和 input. 并且該數(shù)組具有如下特點(diǎn):

第 0 個(gè)項(xiàng)表示正則表達(dá)式捕獲的文本

第 1~n 項(xiàng)表示第 1~n 個(gè)反向引用, 依次指向第 1~n 個(gè)分組捕獲的文本, 可以使用RegExp.$ + "編號(hào)1~n" 依次獲取分組中的文本

index 表示匹配字符串的初始位置

input 表示正在檢索的字符串

無論正則表達(dá)式有無全局標(biāo)示"g", exec 的表現(xiàn)都相同. 但正則表達(dá)式對(duì)象的表現(xiàn)卻有些不同. 下面我們來詳細(xì)說明下正則表達(dá)式對(duì)象的表現(xiàn)都有哪些不同.

假設(shè)正則表達(dá)式對(duì)象為 reg , 檢測(cè)的字符為 string , reg.exec(string) 返回值為 array.

若 reg 包含全局標(biāo)示"g", 那么 reg.lastIndex 屬性表示原字符串中匹配的字符串末尾的后一個(gè)位置, 即下次匹配開始的位置, 此時(shí) reg.lastIndex == array.index(匹配開始的位置) + array[0].length(匹配字符串的長(zhǎng)度). 如下:

var reg = /([a-z]+)/gi,
    string = "World Internet Conference";
var array = reg.exec(string);
console.log(array);//["World", "World", index: 0, input: "World Internet Conference"]
console.log(RegExp.$1);//World
console.log(reg.lastIndex);//5, 剛好等于 array.index + array[0].length

隨著檢索繼續(xù), array.index 的值將往后遞增, 也就是說, reg.lastIndex 的值也會(huì)同步往后遞增. 因此, 我們也可以通過反復(fù)調(diào)用 exec 方法來遍歷字符串中所有的匹配文本. 直到 exec 方法再也匹配不到文本時(shí), 它將返回 null, 并把 reg.lastIndex 屬性重置為 0.

接著上述例子, 我們繼續(xù)執(zhí)行代碼, 看看上面說的對(duì)不對(duì), 如下所示:

array = reg.exec(string);
console.log(array);//["Internet", "Internet", index: 6, input: "World Internet Conference"]
console.log(reg.lastIndex);//14

array = reg.exec(string);
console.log(array);//["Conference", "Conference", index: 15, input: "World Internet Conference"]
console.log(reg.lastIndex);//25

array = reg.exec(string);
console.log(array);//null
console.log(reg.lastIndex);//0

以上代碼中, 隨著反復(fù)調(diào)用 exec 方法, reg.lastIndex 屬性最終被重置為 0.

問題回顧

在 test 方法的講解中, 我們留下了一個(gè)問題. 如果正則表達(dá)式帶有全局標(biāo)志g, 以上 test 方法的執(zhí)行結(jié)果將受 reg.lastIndex影響, 不僅如此, exec 方法也一樣. 由于 reg.lastIndex 的值并不總是為零, 并且它決定了下次匹配開始的位置, 如果在一個(gè)字符串中完成了一次匹配之后要開始檢索新的字符串, 那就必須要手動(dòng)地把 lastIndex 屬性重置為 0. 避免出現(xiàn)下面這種錯(cuò)誤:

var reg = /[0-9]+/g,
    str1 = "123abc",
    str2 = "123456";
reg.exec(str1);
console.log(reg.lastIndex);//3
var array = reg.exec(str2);
console.log(array);//["456", index: 3, input: "123456"]

以上代碼, 正確執(zhí)行結(jié)果應(yīng)該是 "123456", 因此建議在第二次執(zhí)行 exec 方法前, 增加一句 "reg.lastIndex = 0;".

若 reg 不包含全局標(biāo)示"g", 那么 exec 方法的執(zhí)行結(jié)果(array)將與 string.match(reg) 方法執(zhí)行結(jié)果完全相同.

String

match, search, replace, split 方法請(qǐng)參考 字符串常用方法 中的講解.

如下展示了使用捕獲性分組處理文本模板, 最終生成完整字符串的過程:

var tmp = "An ${a} a $ keeps the ${c} away";
var obj = {
  a:"apple",
  b:"day",
  c:"doctor"
};
function tmpl(t,o){
  return t.replace(/${(.)}/g,function(m,p){
    console.log("m:"+m+" p:"+p);
    return o[p];
  });
}
tmpl(tmp,obj);

上述功能使用ES6可這么實(shí)現(xiàn):

var obj = {
  a:"apple",
  b:"day",
  c:"doctor"
};
with(obj){
  console.log(`An ${a} a $ keeps the ${c} away`);
}
正則表達(dá)式在H5中的應(yīng)用

H5中新增了 pattern 屬性, 規(guī)定了用于驗(yàn)證輸入字段的模式, pattern的模式匹配支持正則表達(dá)式的書寫方式. 默認(rèn) pattern 屬性是全部匹配, 即無論正則表達(dá)式中有無 "^", "$" 元字符, 它都是匹配所有文本.

注: pattern 適用于以下 input 類型:text, search, url, telephone, email 以及 password. 如果需要取消表單驗(yàn)證, 在form標(biāo)簽上增加 novalidate 屬性即可.

正則引擎

目前正則引擎有兩種, DFA 和 NFA, NFA又可以分為傳統(tǒng)型NFA和POSIX NFA.

DFA?Deterministic finite automaton 確定型有窮自動(dòng)機(jī)

NFA Non-deterministic finite automaton 非確定型有窮自動(dòng)機(jī)

Traditional NFA

POSIX NFA

DFA引擎不支持回溯, 匹配快速, 并且不支持捕獲組, 因此也就不支持反向引用. 上述awk, egrep命令均支持 DFA引擎.

POSIX NFA主要指符合POSIX標(biāo)準(zhǔn)的NFA引擎, 像 javaScript, java, php, python, c#等語言均實(shí)現(xiàn)了NFA引擎.

有關(guān)正則表達(dá)式詳細(xì)的匹配原理, 暫時(shí)沒在網(wǎng)上看到適合的文章, 建議選讀 Jeffrey Friedl 的 <精通正則表達(dá)式>[第三版] 中第4章-表達(dá)式的匹配原理(p143-p183), Jeffrey Friedl 對(duì)正則表達(dá)式有著深刻的理解, 相信他能夠幫助您更好的學(xué)習(xí)正則.

有關(guān)NFA引擎的簡(jiǎn)單實(shí)現(xiàn), 可以參考文章 基于ε-NFA的正則表達(dá)式引擎 - twoon.

總結(jié)

在學(xué)習(xí)正則的初級(jí)階段, 重在理解 ①貪婪與非貪婪模式, ②分組, ③捕獲性與非捕獲性分組, ④命名分組, ⑤固化分組, 體會(huì)設(shè)計(jì)的精妙之處. 而高級(jí)階段, 主要在于熟練運(yùn)用⑥零寬斷言(或環(huán)視)解決問題, 并且熟悉正則匹配的原理.

實(shí)際上, 正則在 javaScript 中的功能不算強(qiáng)大, js 僅僅支持了①貪婪與非貪婪模式, ②分組, ③捕獲性與非捕獲性分組 以及 ⑥零寬斷言中的順序環(huán)視. 如果再稍微熟悉些 js 中7種與正則有關(guān)的方法(compile, test, exec, match, search, replace, split), 那么處理文本或字符串將游刃有余.

正則表達(dá)式, 在文本處理方面天賦異稟, 它的功能十分強(qiáng)大, 很多時(shí)候甚至是唯一解決方案. 正則不局限于js, 當(dāng)下熱門的編輯器(比如Sublime, Atom) 以及 IDE(比如WebStorm, IntelliJ?IDEA) 都支持它. 您甚至可以在任何時(shí)候任何語言中, 嘗試使用正則解決問題, 也許之前不能解決的問題, 現(xiàn)在可以輕松的解決.

附其他語言正則資料:

Python正則表達(dá)式操作指南

java正則表達(dá)式

本文作者: louis
本文簡(jiǎn)介: 本文斷斷續(xù)續(xù)歷時(shí)兩個(gè)月而成, 共計(jì)12k字, 為求簡(jiǎn)潔全面地還原前端場(chǎng)景中正則的使用規(guī)律, 搜集了大量正則相關(guān)資料, 并剔除不少冗余字句, 碼字不易, 喜歡的請(qǐng)點(diǎn)個(gè)贊?或者收藏, 我將持續(xù)保持更新.
原文地址: http://louiszhai.github.io/20...

參考文章

Jeffrey Friedl 的 <精通正則表達(dá)式>[第三版]

linux shell 正則表達(dá)式(BREs,EREs,PREs)差異比較

正則表達(dá)式之捕獲組/非捕獲組介紹_正則表達(dá)式_腳本之家

正則表達(dá)式(一) -- 元字符 - 逆心 - 博客園

正則表達(dá)式詳解 - guisu,程序人生。 逆水行舟,不進(jìn)則退。 - 博客頻道 - CSDN.NET

正則表達(dá)式之固化分組 - taek - 博客園

正則表達(dá)式之 貪婪與非貪婪模式詳解(概述)_正則表達(dá)式_腳本之家

JAVASCRIPT 正則表達(dá)式學(xué)習(xí)-->基礎(chǔ)與零寬斷言(轉(zhuǎn)自司徒正美) - 隨風(fēng)之羽 - 博客頻道 - CSDN.NET

Unicode與JavaScript詳解 - 阮一峰的網(wǎng)絡(luò)日志

0-9 ?

fnrtv ?

A-Za-z0-9_ ?

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88131.html

相關(guān)文章

  • 前端資源系列(4)-前端學(xué)習(xí)資源分享&前端面試資源匯總

    摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...

    princekin 評(píng)論0 收藏0
  • 正則達(dá)式

    摘要:本文內(nèi)容共正則表達(dá)式火拼系列正則表達(dá)式回溯法原理學(xué)習(xí)正則表達(dá)式,是需要懂點(diǎn)兒匹配原理的。正則表達(dá)式迷你書問世了讓幫你生成和解析參數(shù)字符串最全正則表達(dá)式總結(jié)驗(yàn)證號(hào)手機(jī)號(hào)中文郵編身份證地址等是正則表達(dá)式的縮寫,作用是對(duì)字符串執(zhí)行模式匹配。 JS 的正則表達(dá)式 正則表達(dá)式 一種幾乎可以在所有的程序設(shè)計(jì)語言里和所有的計(jì)算機(jī)平臺(tái)上使用的文字處理工具。它可以用來查找特定的信息(搜索),也可以用來查...

    bang590 評(píng)論0 收藏0
  • 前端技術(shù) 博客文章、書籍 積累

    摘要:好多編輯器例如等都支持這樣的語法來快速的編寫代碼如何優(yōu)雅地使用把標(biāo)簽放在結(jié)束標(biāo)簽之后結(jié)束標(biāo)簽之前的差別什么是響應(yīng)式設(shè)計(jì)怎樣進(jìn)行 書籍 《JavaScriptDOM編程藝術(shù)》《JavaScript高級(jí)程序設(shè)計(jì)》《JavaScript框架設(shè)計(jì)》《JavaScript專家編程》《JavaScript Ninjia》《JavaScript語言精粹(修訂版)》《JavaScript設(shè)計(jì)模式》《J...

    LiangJ 評(píng)論0 收藏0
  • 前端技術(shù) 博客文章、書籍 積累

    摘要:好多編輯器例如等都支持這樣的語法來快速的編寫代碼如何優(yōu)雅地使用把標(biāo)簽放在結(jié)束標(biāo)簽之后結(jié)束標(biāo)簽之前的差別什么是響應(yīng)式設(shè)計(jì)怎樣進(jìn)行 書籍 《JavaScriptDOM編程藝術(shù)》《JavaScript高級(jí)程序設(shè)計(jì)》《JavaScript框架設(shè)計(jì)》《JavaScript專家編程》《JavaScript Ninjia》《JavaScript語言精粹(修訂版)》《JavaScript設(shè)計(jì)模式》《J...

    codercao 評(píng)論0 收藏0
  • 前端技術(shù) 博客文章、書籍 積累

    摘要:好多編輯器例如等都支持這樣的語法來快速的編寫代碼如何優(yōu)雅地使用把標(biāo)簽放在結(jié)束標(biāo)簽之后結(jié)束標(biāo)簽之前的差別什么是響應(yīng)式設(shè)計(jì)怎樣進(jìn)行 書籍 《JavaScriptDOM編程藝術(shù)》《JavaScript高級(jí)程序設(shè)計(jì)》《JavaScript框架設(shè)計(jì)》《JavaScript專家編程》《JavaScript Ninjia》《JavaScript語言精粹(修訂版)》《JavaScript設(shè)計(jì)模式》《J...

    huayeluoliuhen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

zhoutao

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<