一次性搞懂JavaScript正則表達式之語法

本文是『horseshoe·Regex專題』系列文章之一,后續會有更多專題推出
GitHub地址:https://github.com/veedrin/horseshoe
博客地址(文章排版真的很漂亮):https://veedrin.com
如果覺得對你有幫助,歡迎來GitHub點Star或者來我的博客親口告訴我
名余曰正則兮,字余曰靈均。

Regular Expressions翻譯成中文叫正則表達式。也不知道是誰翻譯過來的,聽起來就很嚴肅。似乎翻譯成通用表達式更能傳達其精髓,如果你不怕夢見屈原的話。

為什么叫通用表達式?因為它有一套和編程語言無關的文本匹配規則。很多語言都實現了正則表達式的文本匹配引擎,只不過在功能集合上略有不同。

我們要記住的是三點:

其一,正則表達式是用來提取文本的。

其二,正則表達式的表達能力強大到令人發指。

其三,正則表達式的語法對初學者不友好。

另外,本專題只涉及JavaScript語言的正則表達式,其他語言的規則可能略有不同。

我還為各位讀者準備了一副宣傳語,應該能讓你心動(點贊)吧?

學一門前端工具,幾年就過時了。學正則表達式,受用一輩子。

普通字符

什么叫普通字符?

當我們寫a的時候,我們指的就是a;當我們寫的時候,我們指的就是

'hello ? regex'.match(/?/);
// ["?", index: 6, input: "hello ? regex", groups: undefined]

這就是普通字符,它在正則中的含義就是檢索它本身。除了正則規定的部分字符外,其余的都是普通字符,包括各種人類語言,包括emoji,只要能夠表達為字符串。

開始與結束

^字符的英文是caret,翻譯成中文是脫字符。不要問我,又不是我翻譯的。它在正則中屬于元字符,通常代表的意義是文本的開始。說通常是因為當它在字符組中[^abc]另有含義。

什么叫文本的開始?就是如果它是正則主體的第一個符號,那緊跟著它的字符必須是被匹配文本的第一個字符。

'regex'.match(/^r/);
// ["r", index: 0, input: "regex", groups: undefined]

問題來了,如果^不是正則的第一個符號呢?

'regex'.match(/a^r/);
// null

所以呀,關于它有三點需要注意:

  • 作為匹配文本開始元字符的時候必須是正則主體的第一個符號,否則正則無效。
  • 它匹配的是一個位置,而不是具體的文本。
  • 它在其他規則中有另外的含義。

$字符與^正好相反。它代表文本的結束,并且沒有其他含義(其實是有的,但不是在正則主體內)。同樣,它必須是正則主體的最后一個符號。

'regex'.match(/x$/);
// ["x", index: 4, input: "regex", groups: undefined]

^$特殊的地方在于它匹配的是一個位置。位置不像字符,它看不見,所以更不容易理解。

轉義

我們現在已經知道$匹配文本的結束位置,它是元字符。但是如果我想匹配$本身呢?匹配一個美元符號的需求再常見不過了吧。所以我們得將它貶為庶民。

\反斜杠就是干這個的。

'price: $3.6'.match(/\$[0-9]+\.[0-9]+$/);
// ["$3.6", index: 7, input: "price: $3.6", groups: undefined]

上面的例子有點超綱了,超綱的部分先不管。

你可以認為\也是一個元字符,它跟在另一個元字符后面,就能還原它本來的含義。

如果有兩個\呢?那就是轉義自身了。如果有三個\呢?我們得分成兩段去理解。以此類推。

普通字符前面跟了一個\是什么效果?首先它們是一個整體,然后普通字符轉義后還是普通字符。

帶反斜杠的元字符

一般來說,普通字符前面帶反斜杠還是普通字符,但是有一些普通字符,帶反斜杠后反而變成了元字符。

要怪只能怪計算機領域的常用符號太少了。

元字符含義
b匹配一個單詞邊界(boundary)
B匹配一個非單詞邊界
d匹配一個數字字符(digit)
D匹配一個非數字字符
s匹配一個空白字符(space)
S匹配一個非空白字符
w匹配一個字母或者一個數字或者一個下劃線(word)
W匹配一個字母、數字和下劃線之外的字符

你這么聰明,肯定一眼就看出來,大寫代表反義。對,就是這么好記。

b元字符

\b匹配的也是一個位置,而不是一個字符。單詞和空格之間的位置,就是所謂單詞邊界。

'hello regex'.match(/\bregex$/);
// ["regex", index: 6, input: "hello regex", groups: undefined]
'hello regex'.match(/\Bregex$/);
// null

所謂單詞邊界,對中文等其他語言是無效的。

'jiangshuying gaoyuanyuan huosiyan'.match(/\bgaoyuanyuan\b/);
// ["gaoyuanyuan", index: 13, input: "jiangshuying gaoyuanyuan huosiyan", groups: undefined]
'江疏影 高圓圓 霍思燕'.match(/\b高圓圓\b/);
// null

所以\b翻譯一下就是^\w|\w$|\W\w|\w\W

d元字符

\d匹配一個數字,注意,這里的數字不是指JavaScript中的數字類型,因為文本全是字符串。它指的是代表數字的字符。

'123'.match(/\d/);
// ["1", index: 0, input: "123", groups: undefined]

s元字符

\s匹配一個空白字符。

這里需要解釋一下什么是空白字符。

空白字符不是空格,它是空格的超集。很多人說它是\f\n\r\t\v的總和,其中\f是換頁符,\n是換行符,\r是回車符,\t是水平制表符,\v是垂直制表符。是這樣么?

'a b'.match(/\w\s\w/);
// ["a b", index: 0, input: "a b", groups: undefined]
'a b'.match(/\w\f\w/);
// null
'a b'.match(/\w\n\w/);
// null
'a b'.match(/\w\r\w/);
// null
'a b'.match(/\w\t\w/);
// null
'a b'.match(/\w\v\w/);
// null
'a b'.match(/\w \w/);
// ["a b", index: 0, input: "a b", groups: undefined]

這樣說的人,明顯是沒有做過實驗。其實正確的寫法是空格\f\n\r\t\v的總和,集合里面包含一個空格,可千萬別忽略了。誒,難道空格在正則中的寫法就是空一格么,是的,就是這樣隨意。

這個集合中很多都是不可打印字符,估計只有\n是我們的老朋友。所以,如果不需要區分空格和換行的話,那就大膽的用\s吧。

w元字符

\w匹配一個字母或者一個數字或者一個下劃線。為什么要將它們放一起?想一想JavaScript中的變量規則,包括很多應用的用戶名都只能是這三樣,所以把它們放一起挺方便的。

不過要注意,字母指的是26個英文字母,其他的不行。

'正則'.match(/\w/);
// null

負陰抱陽

如果我們將大寫和小寫的帶反斜杠的元字符組合在一起,就能匹配任何字符。是的,不針對任何人。

'@regex'.match(/[\s\S]/);
// ["@", index: 0, input: "@regex", groups: undefined]

方括號的含義我們先按下不表。

道生一

.在正則中的含義仙風道骨,它匹配換行符之外的任意單個字符。

如果文本不存在換行符,那么.[\b\B][\d\D][\s\S][\w\W]是等價的。

如果文本存在換行符,那么(.|\n)[\b\B][\d\D][\s\S][\w\W]是等價的。

'@regex'.match(/./);
// ["@", index: 0, input: "@regex", groups: undefined]

量詞

前面我們一直在強調,一個元字符只匹配一個字符。即便強大如.它也只能匹配一個。

那匹配gooooogle的正則是不是得寫成/gooooogle/呢?

正則冷笑,并向你發射一個蔑視。

如果匹配的模式有重復,我們可以聲明它重復的次數。

量詞含義
?重復零次或者一次
+重復一次或者多次,也就是至少一次
*重復零次或者多次,也就是任意次數
{n}重復n次
{n,}重復n次或者更多次
{n,m}重復n次到m次之間的次數,包含n次和m次

有三點需要注意:

  • ?在諸如匹配http協議的時候非常有用,就像這樣:/http(s)?/。它在正則中除了是量詞還有別的含義,后面會提到。
  • 我們習慣用/.*/來匹配若干對我們沒有價值的文本,它的含義是若干除換行符之外的字符。比如我們需要文本兩頭的格式化信息,中間是什么無所謂,它就派上用場了。不過它的性能可不好。
  • {n,m}之間不能有空格,空格在正則中是有含義的。

關于量詞最令人困惑的是:它重復什么?

它重復緊貼在它前面的某個集合。第一點,必須是緊貼在它前面;第二點,重復一個集合。最常見的集合就是一個字符,當然正則中有一些元字符能夠將若干字符變成一個集合,后面會講到。

'gooooogle'.match(/go{2,5}gle/);
// ["gooooogle", index: 0, input: "gooooogle", groups: undefined]

如果一個量詞緊貼在另一個量詞后面會怎樣?

'gooooogle'.match(/go{2,5}+gle/);
// Uncaught SyntaxError: Invalid regular expression: /go{2,5}+gle/: Nothing to repeat

貪婪模式與非貪婪模式

前面提到量詞不能緊跟在另一個量詞后面,馬上要??打臉了。

'https'.match(/http(s)?/);
// ["https", "s", index: 0, input: "https", groups: undefined]
'https'.match(/http(s)??/);
// ["http", undefined, index: 0, input: "https", groups: undefined]

然而,我的臉是這么好打的?

緊跟在?后面的?它不是一個量詞,而是一個模式切換符,從貪婪模式切換到非貪婪模式。

貪婪模式在正則中是默認的模式,就是在既定規則之下匹配盡可能多的文本。因為正則中有量詞,它的重復次數可能是一個區間,這就有了取舍。

緊跟在量詞之后加上?就可以開啟非貪婪模式。怎么省事怎么來。

這里的要點是,?必須緊跟著量詞,否則的話它自己就變成量詞了。

字符組

正則中的普通字符只能匹配它自己。如果我要匹配一個普通字符,但是我不確定它是什么,怎么辦?

'grey or gray'.match(/gr[ae]y/);
// ["grey", index: 0, input: "grey or gray", groups: undefined]

方括號在正則中表示一個區間,我們稱它為字符組。

首先,字符組中的字符集合只是所有的可選項,最終它只能匹配一個字符。

然后,字符組是一個獨立的世界,元字符不需要轉義。

'$'.match(/[$&@]/);
// ["$", index: 0, input: "$", groups: undefined]

最后,有兩個字符在字符組中有特殊含義。

^在字符組中表示取反,不再是文本開始的位置了。

'regex'.match(/[^abc]/);
// ["r", index: 0, input: "regex", groups: undefined]

如果我就要^呢?前面已經講過了,轉義。

-本來是一個普通字符,在字符組中搖身一變成為連字符。

'13'.match(/[1-9]3/);
// ["13", index: 0, input: "13", groups: undefined]

連字符的意思是匹配范圍在它的左邊字符和右邊字符之間。

如果我這樣呢?

'abc-3'.match(/[0-z]/);
// ["a", index: 0, input: "abc-3", groups: undefined]
'xyz-3'.match(/[0-c]/);
// ["3", index: 4, input: "xyz-3", groups: undefined]
'xyz-3'.match(/[0-$]/);
// Uncaught SyntaxError: Invalid regular expression: /[0-$]/: Range out of order in character class

發現什么了沒有?只有兩種字符是可以用連字符的:英文字母和數字。而且英文字母可以和數字連起來,英文字母的順序在后面。這和撲克牌1 2 3 4 5 6 7 8 9 10 J Q K是一個道理。

捕獲組與非捕獲組

我們已經知道量詞是怎么回事了,我們也知道量詞只能重復緊貼在它前面的字符。

如果我要重復的是一串字符呢?

'i love you very very very much'.match(/i love you very +much/);
// null
'i love you very very very much'.match(/i love you v+e+r+y+ +much/);
// null

這樣肯定是不行的。是時候請圓括號出山了。

'i love you very very very much'.match(/i love you (very )+much/);
// ["i love you very very very much", "very ", index: 0, input: "i love you very very very much", groups: undefined]

圓括號的意思是將它其中的字符集合打包成一個整體,然后量詞就可以操作這個整體了。這和方括號的效果是完全不一樣的。

而且默認的,圓括號的匹配結果是可以捕獲的。

正則內捕獲

現在我們有一個需求,匹配<div>標簽。

'<div>hello regex</div>'.match(/<div>.*<\/div>/);
// ["<div>hello regex</div>", index: 0, input: "<div>hello regex</div>", groups: undefined]

這很簡單。但如果我要匹配的是任意標簽,包括自定義的標簽呢?

'<App>hello regex</App>'.match(/<([a-zA-Z]+)>.*<\/\1>/);
// ["<App>hello regex</App>", "App", index: 0, input: "<App>hello regex</App>", groups: undefined]

這時候就要用到正則的捕獲特性。正則內捕獲使用\數字的形式,分別對應前面的圓括號捕獲的內容。這種捕獲的引用也叫反向引用

我們來看一個更復雜的情況:

'<App>hello regex</App><p>A</p><p>hello regex</p>'.match(/<((A|a)pp)>(hello regex)+<\/\1><p>\2<\/p><p>\3<\/p>/);
// ["<App>hello regex</App><p>A</p><p>hello regex</p>", "App", "A", "hello regex", index: 0, input: "<App>hello regex</App><p>A</p><p>hello regex</p>", groups: undefined]

如果有嵌套的圓括號,那么捕獲的引用是先遞歸的,然后才是下一個頂級捕獲。所謂深度優先。

正則外捕獲

'@abc'.match(/@(abc)/);
// ["@abc", "abc", index: 0, input: "@abc", groups: undefined]
RegExp.$1;
// "abc"

沒錯,RegExp就是構造正則的構造函數。如果有捕獲組,它的實例屬性$數字會顯示對應的引用。

如果有多個正則呢?

'@abc'.match(/@(abc)/);
// ["@abc", "abc", index: 0, input: "@abc", groups: undefined]
'@xyz'.match(/@(xyz)/);
// ["@xyz", "xyz", index: 0, input: "@xyz", groups: undefined]
RegExp.$1;
// "xyz"

RegExp構造函數的引用只顯示最后一個正則的捕獲。

另外還有一個字符串實例方法也支持正則捕獲的引用,它就是replace方法。

'hello **regex**'.replace(/\*{2}(.*)\*{2}/, '<strong>$1</strong>');
// "hello <strong>regex</strong>"

實際上它才是最常用的引用捕獲的方式。

捕獲命名

這是ES2018的新特性。

使用\數字引用捕獲必須保證捕獲組的順序不變。現在開發者可以給捕獲組命名了,有了名字以后,引用起來更加確定。

'<App>hello regex</App>'.match(/<(?<tag>[a-zA-Z]+)>.*<\/\k<tag>>/);
// ["<App>hello regex</App>", "App", index: 0, input: "<App>hello regex</App>", groups: {tag: "App"}]

在捕獲組內部最前面加上?<key>,它就被命名了。使用\k<key>語法就可以引用已經命名的捕獲組。

是不是很簡單?

通常情況下,開發者只是想在正則中將某些字符當成一個整體看待。捕獲組很棒,但是它做了額外的事情,肯定需要額外的內存占用和計算資源。于是正則又有了非捕獲組的概念。

'@abc'.match(/@(abc)/);
// ["@abc", "abc", index: 0, input: "@abc", groups: undefined]
'@abc'.match(/@(?:abc)/);
// ["@abc", index: 0, input: "@abc", groups: undefined]

只要在圓括號內最前面加上?:標識,就是告訴正則引擎:我只要這個整體,不需要它的引用,你就別費勁了。從上面的例子也可以看出來,match方法返回的結果有些許不一樣。

個人觀點:我覺得正則的捕獲設計應該反過來,默認不捕獲,加上?:標識后才捕獲。因為大多數時候開發者是不需要捕獲的,但是它又懶得加?:標識,會有些許性能浪費。

分支

有時候開發者需要在正則中使用或者

'高圓圓'.match(/陳喬恩|高圓圓/);
// ["高圓圓", index: 0, input: "高圓圓", groups: undefined]

|就代表或者。字符組其實也是一個多選結構,但是它們倆有本質區別。字符組最終只能匹配一個字符,而分支匹配的是左邊所有的字符或者右邊所有的字符。

我們來看一個例子:

'我喜歡高圓圓'.match(/我喜歡陳喬恩|高圓圓/);
// ["高圓圓", index: 3, input: "我喜歡高圓圓", groups: undefined]

因為|是將左右兩邊一切兩半,然后匹配左邊或者右邊。所以上面的正則顯然達不到我們想要的效果。這個時候就需要一個東西來縮小分支的范圍。誒,你可能已經想到了:

'我喜歡高圓圓'.match(/我喜歡(?:陳喬恩|高圓圓)/);
// ["我喜歡高圓圓", index: 0, input: "我喜歡高圓圓", groups: undefined]

沒錯,就是圓括號。

零寬斷言

正則中有一些元字符,它不匹配字符,而是匹配一個位置。比如之前提到的^$^的意思是說這個位置應該是文本開始的位置。

正則還有一些比較高級的匹配位置的語法,它匹配的是:在這個位置之前或之后應該有什么內容。

零寬(zero-width)是什么意思?指的就是它匹配一個位置,本身沒有寬度。

斷言(assertion)是什么意思?指的是一種判斷,斷言之前或之后應該有什么或應該沒有什么。

零寬肯定先行斷言

所謂的肯定就是判斷有什么,而不是判斷沒有什么。

而先行指的是向前看(lookahead),斷言的這個位置是為前面的規則服務的。

語法很簡單:圓括號內最左邊加上?=標識。

'CoffeeScript JavaScript javascript'.match(/\b\w{4}(?=Script\b)/);
// ["Java", index: 13, input: "CoffeeScript JavaScript javascript", groups: undefined]

上面匹配的是四個字母,這四個字母要滿足以下條件:緊跟著的應該是Script字符串,而且Script字符串應該是單詞的結尾部分。

所以,零寬肯定先行斷言的意思是:現在有一段正則語法,用這段語法去匹配給定的文本。但是,滿足條件的文本不僅要匹配這段語法,緊跟著它的必須是一個位置,這個位置又必須滿足一段正則語法。

說的再直白點,我要匹配一段文本,但是這段文本后面必須緊跟著另一段特定的文本。零寬肯定先行斷言就是一個界碑,我要滿足前面和后面所有的條件,但是我只要前面的文本。

我們來看另一種情況:

'CoffeeScript JavaScript javascript'.match(/\b\w{4}(?=Script\b)\w+/);
// ["JavaScript", index: 13, input: "CoffeeScript JavaScript javascript", groups: undefined]

上面的例子更加直觀,零寬肯定先行斷言已經匹配過Script一次了,后面的\w+卻還是能匹配Script成功,足以說明它的零寬特性。它為緊貼在它前面的規則服務,并且不影響后面的匹配規則。

零寬肯定后行斷言

先行是向前看,那后行就是向后看(lookbehind)咯。

語法是圓括號內最左邊加上?<=標識。

'演員高圓圓 將軍霍去病 演員霍思燕'.match(/(?<=演員)霍\S+/);
// ["霍思燕", index: 14, input: "演員高圓圓 將軍霍去病 演員霍思燕", groups: undefined]

一個正則可以有多個斷言:

'演員高圓圓 將軍霍去病 演員霍思燕'.match(/(?<=演員)霍.+?(?=\s|$)/);
// ["霍思燕", index: 14, input: "演員高圓圓 將軍霍去病 演員霍思燕", groups: undefined]

零寬否定先行斷言

肯定是判斷有什么,否定就是判斷沒有什么咯。

語法是圓括號內最左邊加上?!標識。

'TypeScript Perl JavaScript'.match(/\b\w{4}(?!Script\b)/);
// ["Perl", index: 11, input: "TypeScript Perl JavaScript", groups: undefined]

零寬否定后行斷言

語法是圓括號最左邊加上?<!標識。

'演員高圓圓 將軍霍去病 演員霍思燕'.match(/(?<!演員)霍\S+/);
// ["霍去病", index: 8, input: "演員高圓圓 將軍霍去病 演員霍思燕", groups: undefined]

修飾符

正則表達式除了主體語法,還有若干可選的模式修飾符。

寫法就是將修飾符安插在正則主體的尾巴上。比如這樣:/abc/gi

g修飾符

gglobal的縮寫。默認情況下,正則從左向右匹配,只要匹配到了結果就會收工。g修飾符會開啟全局匹配模式,找到所有匹配的結果。

'演員高圓圓 將軍霍去病 演員霍思燕'.match(/(?<=演員)\S+/);
// ["高圓圓", index: 2, input: "演員高圓圓 將軍霍去病 演員霍思燕", groups: undefined]
'演員高圓圓 將軍霍去病 演員霍思燕'.match(/(?<=演員)\S+/g);
// ["高圓圓", "霍思燕"]

i修飾符

iignoreCase的縮寫。默認情況下,/z/是無法匹配Z的,所以我們有時候不得不這樣寫:/[a-zA-Z]/i修飾符可以全局忽略大小寫。

很多時候我們不在乎文本是大寫、小寫還是大小寫混寫,這個修飾符還是很有用的。

'javascript is great'.match(/JavaScript/);
// null
'javascript is great'.match(/JavaScript/i);
// ["javascript", index: 0, input: "javascript is great", groups: undefined]

m修飾符

mmultiline的縮寫。這個修飾符有特定起作用的場景:它要和^$搭配起來使用。默認情況下,^$匹配的是文本的開始和結束,加上m修飾符,它們的含義就變成了行的開始和結束。

`
abc
xyz
`.match(/xyz/);
// ["xyz", index: 5, input: "?abc?xyz?", groups: undefined]
`
abc
xyz
`.match(/^xyz$/);
// null
`
abc
xyz
`.match(/^xyz$/m);
// ["xyz", index: 5, input: "?abc?xyz?", groups: undefined]

y修飾符

這是ES2015的新特性。

ysticky的縮寫。y修飾符有和g修飾符重合的功能,它們都是全局匹配。所以重點在sticky上,怎么理解這個粘連呢?

g修飾符不挑食,匹配完一個接著匹配下一個,對于文本的位置沒有要求。但是y修飾符要求必須從文本的開始實施匹配,因為它會開啟全局匹配,匹配到的文本的下一個字符就是下一次文本的開始。這就是所謂的粘連。

'a bag with a tag has a mag'.match(/\wag/g);
// ["bag", "tag", "mag"]
'a bag with a tag has a mag'.match(/\wag/y);
// null
'bagtagmag'.match(/\wag/y);
// ["bag", index: 0, input: "bagtagmag", groups: undefined]
'bagtagmag'.match(/\wag/gy);
// ["bag", "tag", "mag"]

有人肯定發現了貓膩:你不是說y修飾符是全局匹配么?看上面的例子,單獨一個y修飾符用match方法怎么并不是全局匹配呢?

誒,這里說來就話長了。

長話短說呢,就涉及到y修飾符的本質是什么。它的本質有二:

  • 全局匹配(先別著急打我)。
  • 從文本的lastIndex位置開始新的匹配。lastIndex是什么?它是正則表達式的一個屬性,如果是全局匹配,它用來標注下一次匹配的起始點。這才是粘連的本質所在。

不知道你們發現什么了沒有:lastIndex是正則表達式的一個屬性。而上面例子中的match方法是作用在字符串上的,都沒有lastIndex屬性,休怪人家工作不上心。

const reg = /\wag/y;
reg.exec('bagtagmag');
// ["bag", index: 0, input: "bagtagmag", groups: undefined]
reg.exec('bagtagmag');
// ["tag", index: 3, input: "bagtagmag", groups: undefined]
reg.exec('bagtagmag');
// ["mag", index: 6, input: "bagtagmag", groups: undefined]

咱們換成正則方法exec,多次執行,正則的lastIndex在變,匹配的結果也在變。全局匹配無疑了吧。

s修飾符

這是ES2018的新特性。

s不是dotAll的縮寫。s修飾符要和.搭配使用,默認情況下,.匹配除了換行符之外的任意單個字符,然而它還沒有強大到無所不能的地步,所以正則索性給它開個掛。

s修飾符的作用就是讓.可以匹配任意單個字符。

ssingleline的縮寫。

`
abc
xyz
`.match(/c.x/);
// null
`
abc
xyz
`.match(/c.x/s);
// ["c?x", index: 3, input: "?abc?xyz?", groups: undefined]

u修飾符

這是ES2015的新特性。

uunicode的縮寫。有一些Unicode字符超過一個字節,正則就無法正確的識別它們。u修飾符就是用來處理這些不常見的情況的。

'?'.match(/^.$/);
// null
'?'.match(/^.$/u);
// ["?", index: 0, input: "?", groups: undefined]

?,與同義。

筆者對Unicode認識尚淺,這里不過多展開。

本文是『horseshoe·Regex專題』系列文章之一,后續會有更多專題推出
GitHub地址:https://github.com/veedrin/horseshoe
博客地址(文章排版真的很漂亮):https://veedrin.com
如果覺得對你有幫助,歡迎來GitHub點Star或者來我的博客親口告訴我

Regex專題一覽

? 語法

? 方法

? 引擎

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537471.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537471.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537471.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

dbeaver導出表結構和數據_mall數據庫表結構概覽

mall是一套電商系統&#xff0c;后臺系統主要包括商品管理、訂單管理、營銷管理(運營管理促銷管理)、內容管理、用戶管理等模塊&#xff0c;本文主要對這些模塊的數據庫表結構及功能做大概的介紹。商品管理數據庫表結構功能結構訂單管理數據庫表結構功能結構營銷管理數據庫表結…

成為項目經理需要具備什么條件?

優秀的項目經理不問出生&#xff0c;項目經理沒有硬性條件&#xff0c;個人意愿是關鍵&#xff0c;愿意堅持做下去&#xff0c;不停學習項目管理知識&#xff0c;從0到1&#xff0c;從廣到深&#xff0c;平時刻意練習&#xff0c;不怕出錯。

arima模型 p q d 確定_自回歸移動平均模型(ARMA)

自回歸模型&#xff08;AR&#xff09;&#xff1a;①描述當前值和歷史值之間的關系&#xff0c;用變量自身的歷史時間數據對自身進行預測。②自回歸模型必須滿足平穩性的要求。③移動平均模型&#xff08;MA&#xff09;關注的是自回歸模型中的誤差項的累加。移動平均法能有效…

禪道組成

禪道是PHPmysqlapache的組合&#xff0c;如果通過禪道源代碼安裝部署&#xff0c;則要安裝php環境&#xff0c;mysql等配套的運行環境。 禪道提供了Windows環境下的集成安裝包&#xff0c;該安裝包適合新安裝。 禪道Windows集成包由集成工具禪道源代碼組成&#xff0c;升級的…

Mysql閃回工具之binlog2sql的原理及其使用

生產上誤刪數據、誤改數據的現象也是時常發生的現象&#xff0c;作為運維這時候就需要出來補鍋了&#xff0c;最開始的做法是恢復備份&#xff0c;然后從中找到需要的數據再進行修復&#xff0c;但是這個時間太長了&#xff0c;對于大表少數數據的修復來講&#xff0c;動作太大…

禪道11.0windows本機安裝

為了驗證禪道的某個功能&#xff0c;需要用到生產上的數據&#xff0c;又不能在生產上進行。只能在本地搭建一套禪道&#xff0c;還原生產的數據到本地。 1.下載禪道 生產上用的是禪道11.0&#xff0c;數據庫是11.0版本的數據庫&#xff0c;為了更好兼容&#xff0c;下載禪道…

createtrackbar函數_【3】OpenCV圖像處理模塊(10)inRange函數實現閾值化,HSV圖像分割...

本節使用inRange函數來實現閾值化。跟前面的閾值化方法一樣&#xff0c;只不過在實現時用閾值范圍來替代固定閾值。本節還提供了一種物體檢測的手段&#xff0c;用基于像素值范圍的方法&#xff0c;在HSV色彩空間檢測物體。HSV色彩空間HSV&#xff08;hue&#xff0c;saturatio…

PPT取消自動播放

選中PPT頁面&#xff0c;點擊“切換”&#xff0c;檢查下“設置自動換片時間”&#xff0c;如果勾選了&#xff0c;則去掉。

軟件研發成本估算過程之估算軟件規模概述

通常情況下&#xff0c;規模估算是軟件成本估算過程的起點。估算規模是后續計算軟件項目的工作量、成本和進度的主要輸入&#xff0c;是項目范圍管理的關鍵&#xff0c;因此&#xff0c;在條件允許的情況下&#xff0c;應進行規模估算。在規模估算過程中&#xff0c;需要注意以…

自動駕駛芯片_盤點全球自動駕駛芯片“戰場”參與者

據了解&#xff0c;目前出貨量最大的駕駛輔助芯片廠商Mobileye、Nvidia形成“雙雄爭霸”局面&#xff0c;Xilinx則在FPGA的路線上進軍&#xff0c;Google、地平線、寒武紀向專用領域AI芯片發力&#xff0c;國內四維圖新、全志科技、森國科(國科微)在自動駕駛芯片領域積極布局。…

word文檔頁碼不連續怎么弄

頁碼不連續是因為在不連續頁碼的兩頁之間有分隔符。 第一步&#xff1a;搜索分節符 第二步&#xff1a;看頁碼與頁面是否一致 wps的左下角 如果不一致&#xff0c;則第三步 第三步&#xff1a;設置頁碼&#xff1a;“繼續上一頁編碼”

看完動畫你還敢說不會 快速排序

前言 由于LeetCode上的算法題很多涉及到一些基礎的數據結構&#xff0c;為了更好的理解后續更新的一些復雜題目的動畫&#xff0c;推出一個新系列 -----《圖解數據結構》&#xff0c;主要使用動畫來描述常見的數據結構和算法。本系列包括十大排序、堆、隊列、樹、并查集、圖等等…

java多張圖片合成一張_1分鐘學會“全景照片”拍攝技巧,從單反拍攝到PS合成,收藏備用...

作為一名攝影愛好者&#xff0c;您知道如何才能快速拍出一張全景照片&#xff0c;同時保證高畫質和照片不畸變&#xff1f;比如下面的2張圖片&#xff1a;要想得到這樣的全景照片&#xff0c;千萬不要通過后期裁剪&#xff0c;否則清晰度肯定會大打折扣&#xff01;其實&#x…

Chrome查看cookie

不同版本的Chrome查看cookie的入口位置不同&#xff0c;這里介紹個通用的方法。 1.進入設置頁 2.搜索cookie 3.進入“cookie....”&#xff0c;選擇“查看所有......”

console 速查手冊

// 用于輸出一個 js 對象列表* console.log(obj1 [, obj2, ..., objN); // // 一個 js 字符串&#xff0c;其中包含0或多個不同類型的替代字符串 // console.log(String: %s, Int: %d,Float: %f, Object: %o, str, ints, // floats, obj) // // 也支持模板字符串 // console.lo…

nginx 帶寬_Nginx的Gzip功能

程序員自由之路 | 作者urlify.cn/eyuUVr | 來源什么是HTTP壓縮有時候客戶端和服務器之間會傳輸比較大的報文數據&#xff0c;這時候就占用較大的網絡帶寬和時長。為了節省帶寬&#xff0c;加速報文的響應速速&#xff0c;可以將傳輸的報文數據先進行壓縮&#xff0c;然后再進行…

分享朋友圈QQ空間需要哪些參數

shareTitle(分享標題 &#xff0c; shareDes(分享描述 &#xff0c; shareImg(分享圖片地址&#xff0c; shareUrl(分享地址&#xff0c; shareType(分享類型&#xff0c;微信朋友&#xff1a;WEIXIN、微信朋友圈&#xff1a;WEIXIN_CIRCLE、QQ&#xff1a;QQ)

【今日頭條】【抖音火山】前端開發實習生

今日頭條成立于2012年&#xff0c;致力于成為最懂你的信息平臺&#xff0c;連接人與信息&#xff0c;促進內容的創作和交流。通過技術&#xff0c;來改變整個內容生產、消費領域。 5年的時間內&#xff0c;我們已經成為了一個估值過百億美元&#xff0c;用戶數億&#xff0c;DA…

程序員真的是吃青春飯的嗎?(獻給即將進入職場的程序員們)

又有學生問我&#xff1a;程序員真的是吃青春飯的嗎&#xff1f;我是不是做到三十歲就該考慮轉型了&#xff1f; 我告訴他們&#xff1a; 這是中國的記者們用統計數字造下的一個彌天大謊&#xff0c;當我們看到微軟集團內的許多白發程序員在兢兢業業地工作的時候&#xff0c;我…

這一年多來,阿里Blink測試體系如何從0走向成熟?

2019獨角獸企業重金招聘Python工程師標準>>> 摘要&#xff1a; 引言 Apache Flink是面向數據流處理和批處理的分布式開源計算框架&#xff0c;2016年阿里巴巴引入Flink框架&#xff0c;改造為Blink。2017年&#xff0c;阿里整合了所有流計算產品&#xff0c;決定以B…