1. 程式人生 > >正則的查缺補漏

正則的查缺補漏

多選分支|

var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) ); 
// => ["good"]
也就是說分支也是惰性的,當前匹配上了,後面的不再嘗試了
var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
var string = "#ffbbad #Fc01DF #FFF #ffE";
console.log( string.match(regex) ); 
// => ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]
//所以應該把{6} 寫前面  匹配少到匹配多

(?=p) 和(?!p) 零寬斷言

 零寬斷言:零寬度的匹配,匹配到的內容不儲存在匹配結果中,最終匹配的結果只是一個位置。

Javascript 只支援零度先行斷言
?=p         表示p前面的位置 (理解^)     先行斷言
?!p         表示?=p的反面意思    負向先行斷言
零寬斷言返回的是位置而不是字元
    console.log("abcdefg".match(/ab(?=cd)/g));
    console.log("abcdefg".match(/(?=cd)efg/g));//沒匹配上返回null
    console.log("abcdefg".match(/ab(?=cd)cdefg/g));
var result = "hello".replace(/(?!l)/g, '#');
console.log(result); 
// => "#h#ell#o#"    
找到所有的.min.css檔案的檔名
let str = "a.min.css;.min.css;.css;min.css;b.css;c.min.js;d.css;e.a.min.css";
console.log(str.match(/\w+(?=\.min\.css)/g));

測試一個檔案是否是.css字尾,但是不能用.min.css
var reg=/^(?!.*\.min\.css$).+\.css$/;

reg.test('a.min.css'); // false
reg.test('.min.css'); // false
reg.test('.css'); // false
reg.test('min.css'); // true
reg.test('b.css'); // true
reg.test('c.mining.css'); // true

數字的千位分隔符
'12345678'.replace(/\B(?=(\d{3})+\b)/g,',')

(?=.*[0-9])
翻譯 :  有多個任意字元後面必須包含個數字(至少包含)

(?!^[0-9]{6,12}$)
翻譯:  不能全部是數字(不能全部)

\w

單詞\w  [0-9a-zA-Z_]
\b  單詞邊界

分組

()
match [i]  檢視每一個組的數值
$1  $2   $3  記得加引號包起來
建構函式的全域性屬性$1至$9來獲取

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log(string.replace(regex, "$1 / $2 / $3"));  

非捕獲分組

(?:p)
var regex = /(?:ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) ); 
// => ["abab", "ab", "ababab"]

var data = 'windows 98 is ok';
data.match(/windows (?:\d+)/);  // ["windows 98"]
data.match(/windows (\d+)/);    // ["windows 98", "98"]
它跟() 不同的地方在於,不作為子匹配返回

找到每個單詞的首字母
'my name is epeli'.replace(/(?:^|\s)\w/g, c => c.toUpperCase())

反向引用

\1, \2 ,\3  ...
/([a-z]\d)-\d(123)\1/
指的是每一組小括號就是所謂的一組   \1 就是 [a-z]\d  第一個分組的匹配
當我們不需要使用分組引用和反向引用時,此時可以使用非捕獲分組
/^[+-]?(\d+\.\d+|\d+|\.\d+)$/

可以修改成:

/^[+-]?(?:\d+\.\d+|\d+|\.\d+)$/

位置字元和字元序列優先順序要比豎槓高

^(abc|bcd)$  要用括號包起來
^([abc]{3})+$

字元組中的元字元

var string = "^$.*+?|\\/[]{}=!:-,";
var regex = /[\^$.*+?|\\/\[\]{}=!:\-,]/g;
console.log(string.match(regex));
其中[]    ^   -  需要轉義
豎槓的優先順序最低,即最後運算。
另外關於元字元轉義問題,當自己不確定與否時,儘管去轉義,總之是不會錯的。

優化

獨立出確定字元
    /a+/ 可以修改成 /aa*/
提取公共部分
    比如/^abc|^def/,修改成/^(?:abc|def)/。

    又比如/this|that/,修改成/th(?:is|at)/。
 減少分支的數量,縮小它們的範圍
    /red|read/,可以修改成/rea?d/。此時分支和量詞產生的回溯的成本是不一樣的。但這樣優化後,可讀性會降低的。

正則表示式的四種操作

正則表示式是匹配模式

正則  gi   g全域性i區分大小寫 m多行匹配
exec
注意分組的引數
若檢索成功,返回匹配的陣列,否則返回null
let result = /len/g.exec('hello len!!');

//[ 'len', index: 6, input: 'hello len!!' ]
test
test整體匹配時需要使用^和$
若匹配成功返回true否則返回false
let str = "hello leo!";
let res = /leo/.test(str);   // true

search
若檢索成功,返回第一個與正則物件匹配的字串的起始位置,否則返回-1
let str = "hello leo!";
let res = str.search(/leo/g);  // 6

match
匹配記得加/g
若檢索成功,返回與reg匹配的所有結果的一個數組,否則返回null
str.match(regSS)

驗證(最常用的是test )

判斷一個字串中是否有數字
使用 search
    ~~ 常用來取整,也可以用來轉換成數字型別
    ~~true == 1
    ~~false == 0
    ~~"" == 0
    ~~[] == 0
    ~~undefined ==0
    ~~!undefined == 1
    ~~null == 0
    ~~!null == 1
    ~為取反運算子,如果names.indexOf(name)返回值為-1,即names不包含name,那麼~-1 = 0。其他值取反後值都不為0。結合!~來判斷names是否包含name
    
    
    var string = "abc123";
    console.log( !!~string.search(regex) );
    // => true
    
使用testvar regex = /\d/;

    var string = "abc123";
    console.log( regex.test(string) );
    // => true
使用match

    var regex = /\d/;
    var string = "abc123";
    console.log( !!string.match(regex) );
    // => true
    
使用exec
    var regex = /\d/;
    var string = "abc123";
    console.log( !!regex.exec(string) );
    // => true

切分(在js中常用split)

var regex = /,/;
var string = "html,css,javascript";
console.log( string.split(regex) );
// => ["html", "css", "javascript"]

var regex = /\D/;
console.log( "2017/06/26".split(regex) );
console.log( "2017.06.26".split(regex) );
console.log( "2017-06-26".split(regex) );
// => ["2017", "06", "26"]
// => ["2017", "06", "26"]
// => ["2017", "06", "26"]

提取(正則通常要使用分組引用(分組捕獲)最常用的是match)

match

    var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
    var string = "2017-06-26";
    console.log( string.match(regex) );
    // =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]
    
exec

    var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
    var string = "2017-06-26";
    console.log( regex.exec(string) );
    // =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]
test
    var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
    var string = "2017-06-26";
    regex.test(string);
    console.log( RegExp.$1, RegExp.$2, RegExp.$3 );
    // => "2017" "06" "26"
search
    var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
    var string = "2017-06-26";
    string.search(regex);
    console.log( RegExp.$1, RegExp.$2, RegExp.$3 );
    // => "2017" "06" "26"
replace
    var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
    var string = "2017-06-26";
    var date = [];
    string.replace(regex, function(match, year, month, day) {
        date.push(year, month, day);
    });
    console.log(date);
    // => ["2017", "06", "26"]

替換(replace)

相關API(字串四種支援正則)

String#search

String#split

String#match

String#replace

RegExp#test

RegExp#exec

注意點

match 返回結果的格式,與正則物件是否有修飾符g有關
    沒有g,返回的是標準匹配格式,陣列的第一個元素是整體匹配的內容
    有g,返回的所有匹配的內容
    
exec方法就能解決這個問題,它能接著上一次匹配後繼續匹配
正則例項lastIndex屬性,表示下一次匹配開始的位置
var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
    console.log( regex2.exec(string) );
    console.log( regex2.lastIndex);
    console.log( regex2.exec(string) );
    console.log( regex2.lastIndex);
    console.log( regex2.exec(string) );
    console.log( regex2.lastIndex);
    console.log( regex2.exec(string) );
    console.log( regex2.lastIndex);
    // => ["2017", "2017", index: 0, input: "2017.06.27"]
    // => 4
    // => ["06", "06", index: 5, input: "2017.06.27"]
    // => 7
    // => ["27", "27", index: 8, input: "2017.06.27"]
    // => 10
    // => null
    // => 0
從上述程式碼看出,在使用exec時,經常需要配合使用while迴圈    

split
它可以有兩個引數,第二個引數表示陣列的最大長度
var string = "html,css,javascript";
console.log( string.split(/,/, 2) );
// =>["html", "css"]

replace(很強大,單獨拿出來)

這是因為它的第二個引數,可以是字串,也可以是函式。
第二個引數是字串時
    $1,$2,...,$99 匹配第1~99個分組裡捕獲的文字
    $& 匹配到的子串文字
    $` 匹配到的子串的左邊文字 
    $' 匹配到的子串的右邊文字
    $$ 美元符號
例如,把"2,3,5",變成"5=2+3":
    var result = "2,3,5".replace(/(\d+),(\d+),(\d+)/, "$3=$1+$2");
    console.log(result);
    // => "5=2+3"
又例如,把"2,3,5",變成"222,333,555":
    var result = "2,3,5".replace(/(\d+)/g, "$&$&$&");
    console.log(result);
    // => "222,333,555"
再例如,把"2+3=5",變成"2+3=2+3=5=5":
    var result = "2+3=5".replace(/=/, "$&$`$&$'$&");
    console.log(result);
    // => "2+3=2+3=5=5"
當第二個引數是函式時,我們需要注意該回調函式的引數具體是什麼:
    "1234 2345 3456".replace(/(\d)\d{2}(\d)/g, function(match, $1, $2, index, input) {
        console.log([match, $1, $2, index, input]);
    });
    // => ["1234", "1", "4", 0, "1234 2345 3456"]
    // => ["2345", "2", "5", 5, "1234 2345 3456"]
    // => ["3456", "3", "6", 10, "1234 2345 3456"]