看懂火星文(三)

感謝
本文參考《正則表示式迷你書》
分組和分支結構
分組
括號可以提供分組的功能。/a+/, 標示a出現多次。/(ab)+/, 則是將ab作為一組,表示ab出現多次。
分組引用
使用括號可以實現資料提取和替換操作。我們以匹配日期(yyyy-mm-dd)為例
// 無括號版本 var reg1 = /\d{4}-\d{2}-\d{2}/ 複製程式碼

// 有括號版本 var reg2 = /(\d{4})-(\d{2})-(\d{2})/ 複製程式碼

正則引擎在匹配的過程中,會儲存每一個分組匹配到的資料
提取分組資料
match方法
match接受一個正則表示式作為引數。如果正則表示式中有g標示, 將返回與完整正則表示式匹配的所有結果,但不會返回捕獲組。如果沒有使用g標示,則僅返回第一個完整匹配及其相關的捕獲組。
var regex = /(\d{4})-(\d{2})-(\d{2})/g var string = "2017-06-12 2017-06-12" // ["2017-06-12", "2017-06-12"] // 返回與完整正則表示式匹配的所有結果, 不含分組的結果 string.match(regex) 複製程式碼
var regex = /(\d{4})-(\d{2})-(\d{2})/ var string = "2017-06-12 2017-06-12" // 只返回第一個完整匹配和其分組 // ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12 2017-06-12", groups: undefined] string.match(regex) 複製程式碼
exec方法
exec方法接受一個字串作為引數。如果exec匹配失敗將會返回null, 如果匹配成功exec方法將會返回一個數組。
返回的陣列將完全匹配成功的文字作為第一項,而分組匹配的結果在陣列位置0的後面。返回的結果同時擁有index屬性, 標示了匹配字元為於原始字元的索引。input屬性則是原始的字串。
注意當正則物件是否包含g返回結果是不一樣的。如果正則中含有g標示, 那麼正則物件的lastIndex(下一次匹配開始的位置), 會更新。而如果不含有g, 正則的lastIndex不會更新。
var regex = /(\d{4})-(\d{2})-(\d{2})/ var string = "2017-06-12 2017-06-12" // ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12 2017-06-12", groups: undefined] // regex.lastIndex === 0 regex.exec(string) // ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12 2017-06-12", groups: undefined] // regex.lastIndex === 0 regex.exec(string) 複製程式碼
var regex = /(\d{4})-(\d{2})-(\d{2})/g var string = "2017-06-12 2017-06-12" // ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12 2017-06-12", groups: undefined] regex.exec(string) // ["2017-06-12", "2017", "06", "12", index: 11, input: "2017-06-12 2017-06-12", groups: undefined] // regex.lastIndex === 21 regex.exec(string) 複製程式碼
RegEx
我們也可以通過RegEx全域性的建構函式來獲取分組匹配的結果。建構函式的全域性屬性$1到$9儲存了分組匹配的結果。
var regex = /(\d{4})-(\d{2})-(\d{2})/g var string = "2017-06-12 2017-06-12" // 任何正則操作即可 regex.exec(string) // "2017" RegExp.$1 複製程式碼
替換分組資料
我們可以通過replace方法配合分組匹配的結果, 實現替換分組資料
replace
str.replace(regexp|substr, newSubStr|func)
- regexp 一個正則表示式物件,該正則所匹配的內容會被replace的第二個引數(或者函式的返回值)替換掉
- substr 當str中含有substr, 會被第二個引數替換, 僅有第一個匹配項會被替換
- newSubStr 用於替換掉第一個引數在原字串中的匹配部分的字串。該字串中可以內插一些特殊的變數名(見下表)
- func 函式的返回值將替換掉第一個引數匹配到的結果
變數名 | 含義 |
---|---|
$1, $2, $3…… | 第n個分組匹配的結果 |
$` | 插入當前匹配的子串左邊的內容 |
$' | 插入當前匹配的子串右邊的內容 |
$& | 當前匹配的子串 |
// ":cake:loHel:pizza:" ':cake:Hello:pizza:'.replace(/(Hel)(lo)/, '$2$1') // ":cake:ello:pizza:ello:pizza:" // H被替換為ello:pizza: ':cake:Hello:pizza:'.replace(/H/, "$'") // ":cake::cake:ello:pizza:" // H被替換為:cake: ':cake:Hello:pizza:'.replace(/H/, "$`") 複製程式碼
指定一個函式作為引數
函式的返回值作為替換字串。 (注意:上面提到的特殊替換引數在這裡不能被使用。) 另外要注意的是,如果第一個引數是正則表示式,並且其為全域性匹配模式,那麼這個方法將被多次呼叫,每次匹配都會被呼叫。
// 函式每匹配到一次, 就會被呼叫 ':cake::pizza::hamburger::pizza:'.replace('/:pizza:/g', function () { return ':candy:' }) 複製程式碼
函式的引數 | 含義 |
---|---|
match | 當前匹配的子串 |
p1, p2, …… pn | 匹配的分組資料 |
示例
將'2019-01-01'替換為01/01/2019
var str = '2019-01-01' var reg = /(\d{4})-(\d{2})-(\d{2})/ // 01/01/2019 var result = str.replace(reg, '$2/$3/$1') // 01/01/2019 result = str.replace(reg, function (match, year, month, day) { return `${month}/${day}/${year}` }) 複製程式碼
反向引用
我們現在有一個需求, 使用一個正則表示式匹配如下格式的:date:日期字串
2019-01-01, 2019.01.01, 2019/01/01, 並且前後的分割符號需要保持一致
var reg = /\d{4}(\/|\.|-)\d{2}(\/|\.|-)\d{2}/ 複製程式碼
:point_up_2: 上面的正則達到了最基本的目的, 但是我們還沒有解決前後分割符需要保持一致的問題。
這時我們可以到 反向引用 , \1表示第一個分組所匹配到的具體的字元 , \2, \3同理

var reg = /\d{4}(-|\/|\.)\d{2}\1\d{2}/ 複製程式碼
括號巢狀
var regex = /^((\d)(\d(\d)))\1\2\3\4$/ var string = "1231231233" // 分組一 // \d\d\d \1對應 123 // 分組二 // \d \2 對應 1 // 分組三 // \d\d \3 對應 23 // 分組四 // \d \4 對應3 複製程式碼

\10
\10表示第10個分組呢?還是\1和0?
\10表示的是第十個分組
引用不存在的分組
引用不存在的分組不會報錯。例如\2, \2分組不存在, \2匹配的就是字串"\2"
如果分組後面新增量詞
分組後面有量詞的話,分組最終捕獲到的資料是最後一次的匹配
var str = '12345' var reg = /\d+/ var reg1 = /(\d)+/ // ["12345", index: 0, input: "12345", groups: undefined] str.match(reg) // ["12345", index: 0, input: "12345", groups: undefined] str.match(reg1) 複製程式碼
// \1 應當是匹配的最後一次結果 var regex = /(\d)+ \1/; // false regex.test("12345 1") // true regex.test("12345 5") ); 複製程式碼
非捕獲括號
之前的括號都被稱為捕獲型的分組, 捕獲到的組的資料會在API中引用。如果只想使用括號最原始的功能,不在API和反向引用中使用可以使用非捕獲括號。
var str = 'HelloWorld' var reg1 = /(?:Hello)(?:World)/ var reg2 = /(Hello)(World)/ // ["HelloWorld", index: 0, input: "HelloWorld", groups: undefined] // 捕獲組的資料不會引用 reg1.exec(str) // ["HelloWorld", "Hello", "World", index: 0, input: "HelloWorld", groups: undefined] reg2.exec(str) 複製程式碼
示例
實現trim功能
第一種思路是匹配空格, 然後替換空格為空字串
var reg = /^\s+|\s+$/g var str = 'reg ' str.replace(reg, '') 複製程式碼
第二種思路是使用分組把內容提取出來, 進行替換。
var str = 'reg ' var reg = /^\s+(.*)\s+$/g str.replace(reg, '$1') 複製程式碼
如果使用量詞*, 我們需要使用惰性匹配, 因為(. )\s $中(.*)的部分會匹配除了最後一個空格之外的其他空格
var str = 'reg ' var reg = /^\s*(.*)\s*$/g str.replace(reg, '$1') 複製程式碼
將每個單詞的首字母轉換成大寫
var str = 'hello world' // Hello World // 我們匹配單詞邊界後的第一個字母 str.replace(/\b\w{1}/g, function (word) { return word.toLocaleUpperCase() }) 複製程式碼
匹配成對標籤
我們可以使用反向引用, 我們將前面的標籤作為分組用括號包起來, \1將會是分組匹配的結果。我們這樣就能保證前後兩個標籤的型別是一致的
var reg = /<([^>]+)>\w+<\/\1>/ // true reg.test('<p>123</p>') // false reg.test('<p>123</div>') 複製程式碼