文章首發本人博客,由於格式和圖片解析問題,可以前往 閲讀原文
JavaScript中的正則是Perl的大子集,但Perl內部的一些表達式卻沒有繼承
正則表達式是用於匹配字符串中字符組合的模式(可參考MDN教程)
掃碼關注公粽號,查看更多優質文章
一個例子
使用正則將一個數字以科學計數法進行表示,如:
// 10000000 => 10,000,000
現在用一個正則來解決
const str = '10000000'
const reg = /(?=(\B)(\d{3})+$)/g
str.replace(reg, ',') // 10,000,000
修飾符
正則表達式是由模式和修飾符(可有可無)組成
常用的修飾符有:i、g、m、u、y
-
i:匹配時不區分大小寫str = 'JavaScript' reg = /javascript/i reg.test(str) // => true -
g:匹配時會查找所有的匹配項,而不只是第一個str = 'absKsdblsb' reg = /s/g str.match(reg) // => [s, s, s] -
m:匹配多行(會影響^、$操作符)str = 'ab\nab' reg = /^ab/mg str.match(reg) // => [ab,ab] u:開啓完整的 unicode 支持-
y:粘滯模式str = 'Hello, world!'; reg = /\w+/y; reg.lastIndex = 5; // 在位置 5 精確查找 regexp.exec(str) // => null
字符類
字符類是個特殊的字符,用來匹配字符集中字符
如:匹配字符串中的數字
const str = 'jhsd123fg903d'
// 匹配所有的數字
str.match(/\d/g) // => ['1', '2', '3', '9', '0', '3']
常用的字符表達式見下表:
| 表達式 | 描述 | 含義 |
|---|---|---|
| . | 匹配單個字符,除了換行和行結束符 | (小數點)默認匹配除換行符之外的任何單個字符。例如,/.n/ 將會匹配 "nay, an apple is on the tree" 中的 'an' 和 'on',但是不會匹配 'nay'。如果 s ("dotAll") 標誌位被設為 true,它也會匹配換行符。 |
| \w | 匹配單詞字符 | 匹配一個單字字符(字母、數字或者下劃線)。等價於 [A-Za-z0-9_]。例如, /\w/ 匹配 "apple," 中的 'a',"$5.28,"中的 '5' 和 "3D." 中的 '3'。 |
| \W | 匹配非單詞字符 | 匹配一個非單字字符。等價於 1。例如, /\W/ 或者 /1/ 匹配 "50%." 中的 '%'。 |
| \d | 匹配數字 | 匹配一個數字。等價於[0-9]。例如, /\d/ 或者 /[0-9]/ 匹配"B2 is the suite number."中的'2'。 |
| \D | 匹配非數字字符 | 匹配一個非數字字符。等價於2。例如, /\D/ 或者 /2/ 匹配"B2 is the suite number."中的'B' 。 |
| \s | 匹配空白字符 | 匹配一個空白字符,包括空格、製表符、換頁符和換行符。等價於[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。例如, /\s\w*/ 匹配"foo bar."中的' bar'。經測試,\s不匹配"\u180e",在當前版本Chrome(v80.0.3987.122)和Firefox(76.0.1)控制枱輸入/\s/.test("\u180e")均返回false。 |
| \S | 匹配非空白字符 | 匹配一個非空白字符。等價於 3。例如,/\S\w*/ 匹配"foo bar."中的'foo'。 |
| \b | 匹配單詞邊界 | 匹配一個詞的邊界。一個詞的邊界就是一個詞不被另外一個“字”字符跟隨的位置或者前面跟其他“字”字符的位置,例如在字母和空格之間。注意,匹配中不包括匹配的字邊界。換句話説,一個匹配的詞的邊界的內容的長度是0。(不要和[\b]混淆了)使用"moon"舉例:/\bm/匹配“moon”中的‘m’;/oo\b/並不匹配"moon"中的'oo',因為'oo'被一個“字”字符'n'緊跟着。/oon\b/匹配"moon"中的'oon',因為'oon'是這個字符串的結束部分。這樣他沒有被一個“字”字符緊跟着。/\w\b\w/將不能匹配任何字符串,因為在一個單詞中間的字符永遠也不可能同時滿足沒有“字”字符跟隨和有“字”字符跟隨兩種情況。 |
| \B | 匹配非單詞邊界 | 匹配一個非單詞邊界。匹配如下幾種情況:
(1)字符串第一個字符為非“字”字符 (2)字符串最後一個字符為非“字”字符 (3)兩個單詞字符之間 (4)兩個非單詞字符之間 (5)空字符串 |
| \n | 匹配換行符 | 匹配一個換行符 (U+000A)。 |
...等等其他
例子
-
.字符var str = 'aa1bb' str.match(/(\w)\1.(\w)\2/g) // => ['aa1bb'],此處 `.` 匹配的是 1 str = 'nay, an apple is on the tree' str.match(/.n/g) // => ['an', 'on'],匹配兩個結果,此處`.` 分別是 a, o -
\w字符var str = 'a1ds23;.?]' str.match(/\w/g) // => ['a', '1', 'd', 's', '2', '3'] -
\d字符var str = 'a1ds23;.?]' str.match(/\w/g) // => ['1', '2', '3'] -
\s字符var str = 'a1 ds2 3;.?]' str.match(/\w/g) // => [' ', ' '],共有兩處 -
\b字符var str = '2split1;word3' str.match(/\b\d/g) // => [2],先匹配數字[2,1,3],而後單詞邊界的是 [2] -
\B字符var str = '2split1;word3' str.match(/\B\d/g) // => [1, 3],先匹配數字[2,1,3],而後非單詞邊界的是 [1,3]
複用
複用即量詞,常見的量詞包括:*、+、?、{n},量詞可以在匹配多個相同的模式的時候很管用,避免了重複寫相同的模式
一個小例子:
str = 'ab1c2d345e'
str.match(/\d/ig) // => [1, 2, 3, 4, 5]
// 使用量詞 (這裏涉及到了貪婪原則,後面會講到)
str.match(/\d+/ig) // => [1, 2, 345]
來看下面的解釋
-
*:匹配前面的模式0個或多個,相當於{0, n}// 還是上面的例子 // 使用 * str.match(/\d*/ig) // => ['', '', '1', '', '2', '', '345', '', ''] // 這裏因為 * 0個也會匹配,所以每個位置都會匹配到空字符 -
+:匹配前面的模式1個或多個,相當於{1, n}// 使用 +,同理,至少得一個才能匹配上 str.match(/\d+/ig) // => ['1', '2', '345'] -
?:匹配前面模式0個或1個,相當於{0, 1}str = 'Should I write color or colour' // 這裏 ? 表示前面的 u 有或者沒有都行 str.match(/colou?r/ig) // => [color, colour] -
{n}:表示一個範圍,可以是{0,n},{n,} 或者{n,m},表示知道得有左位數和最多位數str = 'erabuiababidjabababkjlsdababababkl' // 至少得有2個ab,最多匹配3個ab str.match(/(ab){2,3}/ig) // => [abab, ababab, ababab]
選擇、組合、捕獲、引用
選擇
選擇是由|標識進行匹配,可以理解為or,如:a|b表示匹配a或者b
str = 'absdfg'
// 匹配s或者d
str.match(/s|d/ig) // => ['s', 'd']
組合(字符集合範圍)
組合在常用的匹配模式中還是常用的,[]用來組合匹配的範圍或者其他
幾個重要概念:
[^..]表示非組合的內容,^在開頭代表不是後面要匹配的內容-
[\b]匹配一個退格,不要和\b混淆了// 匹配第一位是字符全是大寫或者全是小寫的字母 reg1 = /^[A-z]\d*/ig reg1.test('s12314') // => true // 匹配第一位是字母或者數字的,最後一個第一位是字母或者0-2之間的數字,很明顯false reg2 = /^[A-z|\d]\d*/ig reg2.test('s12314') // => true reg2.test('112314') // => true reg3 = /^[A-z|0-2]\d*/ig reg3.test('312314') // => false // 匹配第一位不是字母 reg4 = /^[^A-z]\d*/ig reg4.test('s12314') // => false reg4.test(';12314') // => true
捕獲
捕獲在匹配表達式中經常看到,他的作用非常強大,使用()一對括號進行修飾,這樣就可以捕獲到匹配到裏面的內容,捕獲經常和引用或者修飾符(*、+...)結合使用
在講用法前,需要補充一下排除捕獲,排除捕獲也很重要很常用,可以用一些需要()但又不需要捕獲後的值,用?:排除捕獲
常見用法:
- 使用捕獲獲取具體內容
RegExp的靜態屬性$
str = 'I like Java coding'
// 忽略大小寫
reg = /\w*(JAVA)\w*/ig
// 匹配字符
reg.test(str)
// 匹配後,捕獲到的內容會按照捕獲的順序出現在 `RegExp`的靜態屬性上,以`$`開頭
console.log(RegExp.$1) // => Java
console.log(RegExp.$2) // => undefined ,只有一個捕獲,不存在`$2`屬性
// 使用`replace`方法
str.replace(reg, 'PHP') // => I like PHP coding
str.replace(reg, '【$1】') // => I like 【Java】 coding
str.replace(reg, '$1Script') // => I like JavaScript coding
str.replace(reg, ($, $1) => {
console.log('匹配到了' + $1)
return `【${$1}】`
}) // => I like 【Java】 coding 並且會打印 `匹配到了Java`
// 搜索段落標紅關鍵字
str.replace(reg, '<span style="color:red">$1</span>') // => I like <span style='color:red'>Java</span> coding
- 匹配表達式結合
引用使用
引用其實和前面講到的RegExp的靜態屬性類似,只不過引用是在匹配表達式中使用,寫法不一樣。在匹配表達式中使用 \1 \2...斜槓加一個數字的形式引用所有引用的第一個,類似 $1、$2...
引用的順序與()的使用順序相關聯,規則:由左到右,由外到內,遇到排除捕獲跳過當前()
str = 'qaabbccddq'
reg = /(\w)\1/ig
str.match(reg) // => ['aa', 'bb', 'cc', 'dd']
str = 'qaaaabbbb11q'
reg = /(\w\w)\1(\d)\2?/ig
str.match(reg) // => ['bbbb11']
// 這裏注意是 `\3` 而不是 `\2`
reg1 = /(\w\w)\1((\d)\3)?/ig
str.match(reg1) // => ['aaaa', 'bbbb11']
// 這裏注意使用了 `?:` 排除捕獲,所以是 `\2` 而不是 `\3`
reg2 = /(\w\w)\1(?:(\d)\2)?/ig
str.match(reg1) // => ['aaaa', 'bbbb11']
引用
引用在匹配表達式中使用 \1 這種以 斜槓和數字 結合的方式使用,需要注意的是,引用的數字和使用 ()的規則相關,規則為:由左到右,由外到內,遇到排除捕獲跳過當前(),這一點很重要
關於引用的用法,詳見前面 捕獲小結的使用方法即可
斷言
通常我們要匹配的內容後面要跟着指定的內容,常見的斷言x(?=y)、x(?!y)、(?<=y)x、(?<!y)x
前瞻斷言
前瞻斷言主要是x(?=y)和x(?!y),x(?=y)表示匹配後面緊跟着y的x,x(?!y)表示匹配後面不是y的x
str = '1 candy costs 50€'
// 假如要匹配後面是 `€` 的數字
str.match(/\d+(?=€)/) // => [50]
// 假如要匹配後面不是 `€` 的數字
str.match(/\d+(?!€)/) // => [1]
後瞻斷言
後瞻斷言主要是(?<=y)x和(?<!y)x,(?<=y)x表示匹配前面緊跟着y的x,(?<!y)x表示匹配前面不是y的x
str = '1 candy costs $50'
// 假如要匹配前面是 `$` 的數字
str.match(/(?<=\$)\d+/) // => [50]
// 假如要匹配前面不是 `$` 的數字
str.match(/(?<!\$)\d+/) // => [1]
貪婪與惰性
正則的量詞通常都是遵循貪婪原則,也就是有量詞的時候,儘量匹配多的。而惰性則和這種原則相反,儘量匹配少,不匹配多。
惰性使用?緊跟在量詞的後面,如:
`\d+?` // 匹配一個數字就行
`\w{1,}?` // 匹配1個字母就行
`\d*?` // 匹配0個數字 => ['', ...]
例子
// 匹配帶 `""`號的水果
str = 'I like "apples" a little more than "oranges"'
// 1.貪婪
// 會發現結果並不理想,原因就是默認是貪婪原則,能匹配多的不匹配少的
// 首先,會匹配到第一個`"`
// 而後匹配下一個`"`,當匹配到`apples"`,還會繼續往後匹配
// 看剩下的能不能匹配到 `.+"`,最後匹配到了`oranges"`(當然前面還有`than "`)
str.match(/".+"/g) // => ["apples" a little more than "oranges"]
// 2.惰性
str.match(/".+?"/g) // => ["apples", "oranges"]
// 3.其他方法
str.match(/"[^"]+"/g) // => ["apples", "oranges"]
RegExp屬性、方法
屬性
常見的屬性:source,global,ignoreCase,multiline,lastIndex等等...
-
source
表示正則對象的原表達式內容以字符串的形式輸出,不包含修飾符reg = /(\d\s)\1/ig reg.source // => '(\\d\\s)\\1' -
global
表示是否匹配全部內容,而不是匹配到第一個就停止匹配,返回Boolean類型reg = /(\d\s)\1/ig reg.global // => true -
ignoreCase
表示匹配是否忽略大小寫,返回Boolean類型reg = /(\d\s)\1/ig reg.ignoreCase // => true -
multiline
表示多行匹配,有回車符,換下一行從頭匹配reg = /(\d\s)\1/ig reg.multiline // => false -
lastIndex
在全局匹配時,表示當前匹配的的索引,初始為0,當匹配到結果時,lastIndex值變為最新匹配的位置,如果不能匹配到結果了,然後重0開始str = 'day by day' reg = /\w+/g reg.exec(str) // => ['day', index: 0, input: 'day by day', groups: undefined] reg.lastIndex // => 3 reg.exec(str) // => ['by', index: 4, input: 'day by day', groups: undefined] reg.lastIndex // => 6 reg.exec(str) // => ['day', index: 7, input: 'day by day', groups: undefined] reg.lastIndex // => 10 reg.exec(str) // => null reg.lastIndex // => 0,重新從頭開始方法
常見的方法:exec、test
- exec
一個在字符串中執行查找匹配的RegExp方法,它返回一個數組(未匹配到則返回 null)
同上lastIndex例子 -
test
一個在字符串中測試是否匹配的RegExp方法,它返回 true 或 false。str = '1a1aklk2b2bp' reg = /(\d\w)\1+/ reg.test(str) // => true
String常見的正則方法
常用的方法:replace、match、matchAll、search、split
- replace:字符串替換指定的內容(表達式匹配)
str = 'I like Java coding'
str.replace(/JAVA/i, 'PHP') // => 'I like PHP coding'
str.replace(/(JAVA)/i, '<$1>') // => 'I like <Java> coding'
str.replace(reg, ($, $1) => {
console.log('匹配到了' + $1)
return `【${$1}】`
}) // => I like 【Java】 coding 並且會打印 `匹配到了Java`
- match:匹配所有符合表達式的內容,返回數組,否則null
str = 'd1sd2asf43d5'
str.match(/\d+/) // => [1]
str.match(/\d+/g) // => [1, 2, 43, 5]
str.match(/\d\b/) // => [5]
str.match(/\d\B/) // => [1, 2, 43]
str.match(/\d\s/) // => null
-
matchAll:返回匹配正則表達式的迭代器,參數為
RegExp類型,且必須帶上 修飾符g,否則報錯;其匹配結果返回一個RegExpStringIterator類型的迭代器str = 'd1sd2asf43d5' iterator = str.matchAll(/\d+/g) // => RegExpStringIterator {} Object.prototype.toString.call(iterator) // => [object RegExp String Iterator] // 返回結果基本和迭代器一致,value是類數組 iterator.next() // => {value: [1], done: false} iterator.next() // => {value: [2], done: false} iterator.next() // => {value: [43], done: false} iterator.next() // => {value: [5], done: false} iterator.next() // => {value: undefined, done: true}
- search:匹配正則表達式,如果匹配成功返回第一個匹配的位置,否則返回-1
str = '1 apple costs $50'
str.search(/(?<=\$)\d+/) // => 15
str.search(/\d+/) // => 0
str.search(/\d[A-z]/) // => -1
- split:將以正則匹配的內容進行分割
str = 'skJavaslkdJAVAllksdfJaVAlp'
// 將會以 java 或者 javas 進行分割(不區分大小寫)
str.split(/javas?/i) // => ['sk', 'lkd', 'llksdf', 'lp']
- A-Za-z0-9_ ↩
- 0-9 ↩
- \f\n\r\t\v\u00a0\u1680\u180e\u2000-u200a\u2028\u2029\u202f\u205f\u3000\ufeff ↩