探究javascript中的正則表達式

正則


小編自己也在學習正則,分享給大家一起學習,本篇文章適用于對正則還不是了解的初級選手,廢(xia)話(che)不(dan)多說,直接進入正題。

正則表達式定義

我們知道在js中正則表達式用RegExp對象表示,可以使用RegExp()構造函數來創建RegExp對象,但是在更多的情況下我們是用直接量的語法來創建。
栗子:

//使用直接量的形式
var  reg = /\w$/;
//使用構造函數的形式
var reg = new RegExp('\w$');

但我們如果有動態生成一個正則表達式的話,就需要我們用構造函數的形式了,我們又舉個栗子:

var fmt = 'AA-bb cc:dd:ee';
var date = new Date();
var fmtObj = {
'A+': data.getMonth() + 1,
'b+': date.getDate(),
'c+': dategetHours(),
'd+': date.getMinutes(),
'e+': date.getSeconds(),
}
for(let key in fmtObj) {
 if(new RegExp(`(${key})`).test(fmt)){
fmt = fmt.repalce(RegExp.$1, RegExp.$1.lengt === 1 ? fmtObj[key] : ('00' + fmtObj[key]).substr((fmtObj[key] + '').length))
 }
}

那么什么是直接量字符呢?

直接量字符

正則表達式中所有的字母和數字都是按照字面量的含義進行匹配的,js的正則表達式也支持非字母的字符匹配,這些字符需要通過反斜杠\進行轉譯。下列表格中列舉一些

名稱 內容
\o NUL字符(\u0000)
\t 制表符(\u0009)
\n 換行符 (\u000A)
\v 垂直制表符(\u000B)
\f 換頁符(\u000C)
\r 回車符(\u000D)
\xnn 由16進制數nn指定的拉丁字符,例如,\x0A等價于\n
\uxxxx 由16進制數xxxx指定的unicode字符,例如\u0009等價于\t
\cX 控制字符^X,\cJ等價于換行符\n
字母和數字字符 匹配自身

特殊含義的符號和字符

在正則表達式中,許多標點符號具有特殊的含義,他么是:^ $ . * + ? = ! : | / \ ( ) { } [ ];

某些符號只能在正則表達式的某些上下文中才會具有某種的特殊意義,在其他的上下文當中則會被直接當成直接量處理。如果想要在正則表達式中使用這些符號的直接量進行匹配,則必須使用前綴\, 這是一條通用的規則,其他標點符號(例如@和引號)沒有特殊含義,在正則表達式中按照字面量含義進行匹配,如果不記得哪些標點符號需要反斜杠轉義,可以在每個標點符號前面都加上轉義符\。另外需要注意,許多字母和數字在有反斜杠做前綴時會有特殊含義,所以對于想按照直接量進行匹配的數字和字母,盡量不要用\進行轉義

字符類

將直接量字符單獨放進中括號內就組成了字符類。一個字符類可以匹配它所包含的任意字符,因此表達式/[abc]/就和字母abc中的任意一個都匹配

var reg = /[a-z0-9]/;

上面的正則表達式可以匹配任意一個字母或數字,其中a-z表示a-z的所有字母,0-9表示0到9之間的數字

反字符類[^...]

就是匹配字符類中不含有的字符,用^符號開頭

var reg = /[^a-z0-9]/;

上面的正則表達式的意思就是匹配任意一個不含有字母和數字的字符

正則表達式的字符類

符號 內容
[...] 字符類
[^...] 反字符類
. 除換行符和其他unicode行終止符之外的任意字符
\w 數字,字母和下劃線, 等價于[a-z0-9_]
\W 非數字,字母和下劃線, 等價于[^a-z0-9_]
\s 任何unicode 空白符
\S 非任何unicode 空白符
\d 數字
\D 非數字
\b 單詞邊界
\B 非單詞邊界

正則表達式的重復語法

符號 內容
{n, m} 最少匹配n次,最多匹配m次
{n, } 至少匹配n次或更多
{n} 匹配n次
? 匹配0次或一次
+ 最少匹配一次
* 匹配0次或多次

非貪婪模式

上面我們列舉出了匹配重復字符是盡可能多的匹配,而且允許后續的正則表達式繼續匹配。因此我們稱之為'貪婪'匹配模式。我們同樣可以使用的正則表達式進行非貪婪匹配。只需要在待匹配的字符后跟隨一個問號即可:'??', '+?', '*?' 或者'{1, 5}?'

 var a = /[a-z0-9]+?/
  a.exec('sdfff900') // ["s", index: 0, input: "sdfff900", groups: undefined]
  
  // 使用非貪婪模式匹配得到的結果可能和期望并不一樣,可以來看看看下面的這個例子:
  var b = /a+?b/;
  b.exec('aaab') // ["aaab", index: 0, input: "aaab", groups: undefined]


  var c = /shen(ab)+?/;
  b.exec('shenababab'); // ["shenab", "ab", index: 0, input: "shenababab", groups: undefined]

這是因為正則表達式的模式匹配總是會尋找字符串中第一個可能匹配的位置,由于這個匹配是從字符串的第一個字符開始的,因此在這里不考慮他的子串中更短的匹配

選擇,分組和引用

正則表達式語法還包括指定選擇項,子表達分組和引用前一子表達式的特殊字符,我們分別來看一下
選擇項: |用于分隔供選擇的字符。
/ab|cd|ef/,可以匹配串ab,也可以匹配cd或者是ef。
注意選擇項的嘗試匹配的次序是從左到右,直到發現了匹配的項。如果左邊的選擇項匹配,就忽略右邊的選擇項,即使產生更好的匹配。所以,當正則表達式/a|ab/匹配字符串ab時,他只能匹配第一個字符串a

子表達式:

正則表達式中的圓括號有多種作用。

把單獨的項組合成子表達式,以便可以像處理單獨的單元那樣用 | * + ? 等來對單元內的項進行處理。
var a = /java(script)?/; // 可以匹配字符串 'java',其后可以有'script'也可以沒有。
  var b = /(ab|cd)+|ef/; // 可以匹配字符串ef, 也可以匹配ab或者cd的一次或多次重復
在完整的模式中定義子模式,當一個正則表達式和目標字符串成功匹配時,可以從目標字符串中拿到和圓括號中的字符模式相匹配的部分。
var a = /[a-z]+(\d+)/; // 那么這個時候我們可以從檢索到的匹配中拿到和圓括號中子模式相匹配的數字

()表示捕獲分組,()會把每個分組里的匹配的值保存起來,使用$n(n是一個數字,表示第n個捕獲組的內容)

var reg = /\b(shen(sxx))/;
  reg.test('shensxx');
  console.log(RegExp.$1, RegExp.$2) // shensxx sxx
  
  reg.exec('shensxx');
  // ["shensxx", "shensxx", "sxx", index: 0, input: "shensxx", groups: undefined]

(?:)表示非捕獲分組,和捕獲分組唯一的區別在于,非捕獲分組匹配的值不會保存起來。

var reg = /\b(shen(?:sxx))/;
  reg.test('shensxx');
  console.log(RegExp.$1, RegExp.$2) // shensxx
  
  reg.exec('shensxx');
  // ["shensxx", "shensxx", index: 0, input: "shensxx", groups: undefined]

引用:在同一正則表達式的后部引用前面的子表達式
這是通過在字符\后面加一位數字或多位數字來實現的,這個數字指定了圓括號的子表達式在正則表達式中的位置。\1,\3分別表示正則表達式中的第一個圓括號的子表達式和第三個子表達式(注意這里指的是自表達式中匹配的文本的引用,并不是指子表達式)
注意: 因為子表達式可以嵌套另一個子表達式,所以他們的位置是參與技術的左括號的位置

var a = /([Jj]ava([Ss]cript)?)\sis\s(fun\w*)/; // \2 則是代表的([Ss]sript)

    var b = /(['"])[^'"]*\1/; // 只能匹配單引號或者雙引號是成對出現的,不允許出現一個單引號一個雙引號

指定匹配位置

有一些正則表達式匹配的是字符串之間的位置,而不是實際的字符。例如\b匹配一個單詞邊界,即位于\w\W之間的邊界,或者是一個字符串開始或結束的位置。像\b這樣放任元素不匹配某個可見的字符,他們指定匹配發生的合法位置。還有一些錨元素^ $, 分表表示匹配字符串開始和結束的位置。
\b 匹配一個單詞邊界,即位于 \w 和 \W 之間的邊界,或者是一個字符串開始或結束的位置。

var reg = /xx\b/; 
  reg.test('sxx shen') // true
  reg.test('xxs shen') // false
      
  var reg = /\bsx/;
  reg.test('sxx shen') // true
  reg.test('xsx shen') // false

  var reg = /\b\d+/;
  reg.test('.123') // true 
  reg.test('sxx124') // false

\B 匹配非單詞邊界。er\B 能匹配 verb 中的 er,但不能匹配 never中的 er。
x(?=y) :正向先行斷言,匹配 x 僅僅當 x 后面不跟著 y
x(?!y) :負向先行斷言, 匹配 x 僅僅當x 后面不跟著 y

我們看下面的正則表達式,意思就是非單詞邊界后面跟這三個數字字符串,而且三個數字字符串后面不再跟數字

 var reg = /\B(?=(\d{3})+(?!\d))/g;
  '123456789.123456'.replace(reg, ',') // "123,456,789.123,456"

(?<=y)x :正向后行斷言,匹配 x 僅僅當x 前面跟著 y

var reg = /(?<=95|98|NT|2000)Windows/;
  
  '3.1Windows'.replace(reg, 'aaaa'); // '3.1Windows'沒有匹配到
  
  '2000Windows'.replace(reg, 'aaaa'); // '2000aaaa'

(?<!y)x:負向后行斷言,匹配 x 僅僅當 x 前面跟的不是 y

var reg = /(?<!95|98|NT|2000)Windows/;
  
  '3.1Windows'.replace(reg, 'aaaa'); // '3.1aaaa'
  
  '2000Windows'.replace(reg, 'aaaa'); // '2000Windows' 沒有匹配到

看個demo,不在小數點后加千分符

  var reg = /(?<!\.\d*)\B(?=(\d{3})+(?!\d))/g
  
  '123456789.98764525437'.replace(reg, ',')
  
  // "123,456,789.98764525437"
修飾符

i: 執行時不區分大小寫
g: 執行一個全局匹配,簡而言之,就是找到所有的匹配,而不是在找到第一個之后就停止
m: 多行匹配模式,^匹配一行的開頭和字符串的開頭,$ 匹配行的結束和字符串的結束
使用字面量形式時: /\bjavascript\b/ig
構造函數形式: new RegExp('\bjavascript\b', 'ig')
用于模式匹配的String方法,string支持4種使用正則表達式的方式
search() 不支持全局匹配,會忽略修飾符g


@params: 一個正則表達式,如果參數不是一個正則表達式,那么會通過 `RegExp` 構造函數轉成正則表達式
@return: 第一個與之匹配的字符串的起始位置,若是沒有發生匹配就返回數字 `-1`
  'fsfsdjavsssjavasdfsfsjfjhhshh3r98u'.search(a) // 11
 
  var a = /[Jj]ava([Ss]crit)*/g;
  '012345javascript 67890'.search(a) // 6

replace()

@params: RegExp | string (正則表達式,可以設置修飾符)
@params: function | string
@return: 返回一個新的字符串,不改變源對象

如果第一個參數是一個string,那么 replace() 將直接搜索這個字符串然后進行替換(注意這里指替換第一個搜索到的結果),并不會先轉成 RegExp 進行匹配。
如果第一個參數是一個 RegExp,那么 replace()將會進行正則匹配,將第一次匹配到結果進行替換,這里正則表達式可以設置修飾符。g 會進行全局多次匹配,將所有匹配到的結果進行替換
如果第二個參數是一個 string,那么 replace() 將匹配的結果直接用這個字符串替換
如果第二個參數是一個 function,我們看看可以有哪些參數:
@params: 完整模式匹配到的結果
@params: 完整模式下的子模式匹配到的結果,看正則表達式中有多少個圓括號,那么這里就可以有多少個這樣的參數,每個參數表示對應的子模式匹配的結果
@return: 使用 return 的返回值替換匹配到的值

var a = /([Jj]ava([Ss]cript))\sis\s(fun\w*)/g;
    var b = 'ffhfhjavascript is functionsdfffsff';
    
    b.replace(a, function(match) {
        // 打印的結果就是 javascript is functionsdfffsff 123
        console.log(match, 123);
        return '@@@@'
    });
    // ffhfh@@@@

    b.replace(a, function(match, v1, v2, v3) {
        // 打印的結果就是 javascript is functionsdfffsff 123
        console.log(match, 123);
        // javascript
        console.log(v1);
        // script
        console.log(v2);
        // functionsdfffsff
        console.log(v3);
    
        return '@@@@'
    });
    // ffhfh@@@@

match()

@params: 正則表達式 | string(通過 `RegExp` 的構造函數轉化成正則表達式)
@return: 數組,

如果這個正則表達式沒有設置修飾符 g,match() 就不會進行全局檢索,只檢索第一個匹配。在這種情況下:數組的第一個元素就是就是完整模式匹配的結果,其余的元素則是正則表達式中圓括號的子表達式匹配的結果(如果沒有圓括號就不會有這些元素)。index 表示匹配的位置,input 表示目標字符串

 var a = /([Jj]ava([Ss]cript))\sis\s(fun\w*)/;
  var b = 'ffhfhjavascript is functionsdfffsff';
  
  b.match(a)
  // ["javascript is functionsdfffsff", "javascript", "script", "functionsdfffsff", index: 5, input: "ffhfhjavascript is functionsdfffsff", groups: undefined]

如果添加了修飾符g,那么返回的數組中就是多次全局模式匹配的結果

 var a = /123(sxx)456(hello)/g;
  var b ='123sxx456helloffffjfljfj123sxx456hellojjweww123456hello123sxx456hello';
  
  b.match(a)
  // ["123sxx456hello", "123sxx456hello", "123sxx456hello"]

RegExp的兩種方法:exec() 和 test()

exec()

@params: string
@return: 數組 | null

如果匹配到結果就返回一個數組,結果和字符串的 match() 方法的非全局匹配返回的結果一樣。但是有一點和 match() 不一樣,就是不管式全局還是非全局都是一樣的結構。如果沒有匹配到結果就返回 null

  var a = /123(sxx)456(hello)/g;
  var b ='123sxx456helloffffjfljfj123sxx456hellojjweww123456hello123sxx456hello';
  a.exec(b);
  // [“123sxx456hello", "sxx", "hello", index: 0, input: "123sxx456helloffffjfljfj123sxx456hellojjweww123456hello123sxx456hello", groups: undefined]

test()

@params: string
@return: boolean

這個方法很簡單,匹配成功就返回true,否則就返回false


參考資料

JavaScript權威指南
https://juejin.im/post/5d0c5f47e51d4550bf1ae87b

文中如有錯誤的,望讀者斧正

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

推薦閱讀更多精彩內容

  • Python中的正則表達式(re) import rere.match #從開始位置開始匹配,如果開頭沒有則無re...
    BigJeffWang閱讀 7,123評論 0 99
  • 正則表達式到底是什么東西?字符是計算機軟件處理文字時最基本的單位,可能是字母,數字,標點符號,空格,換行符,漢字等...
    獅子挽歌閱讀 2,161評論 0 9
  • 假設用戶需要在HTML 表單中填寫姓名、地址、出生日期等。那么在將表單提交到服務器進一步處理前,JavaScrip...
    胖先森閱讀 364評論 0 2
  • 感恩訊飛語記,讓我每次寫感恩日記都非常的方便,訊飛語系確實非常好用,識別度也非常的高,讓我在寫日記的時候比較放心的...
    旅行者的心境主動人生閱讀 90評論 0 0
  • 2018年5月17日 星期四 晴 今早上我帶著閨女去了趟早市,蔬菜都好新鮮?,F在正是蘆筍上市的季節...
    左手繁花閱讀 146評論 0 1