正则表达式相关

Posted by Qz on August 21, 2017

“Yeah It’s on. ”

正文

网页链接

test

test() 方法用于检测一个字符串是否匹配某个模式.

RegExpObject.test(string)

string 必需。要检测的字符串。

返回值 如果字符串 string 中含有与 RegExpObject 匹配的文本,则返回 true,否则返回 false。

var str = "Visit W3School";
var patt1 = new RegExp("W3School");
var result = patt1.test(str);
document.write("Result: " + result);

replace

replace#指定一个函数作为参数

replace() 方法返回一个由替换值(replacement)替换部分或所有的模式(pattern)匹配项后的新字符串。模式可以是一个字符串或者一个正则表达式,替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。如果pattern是字符串,则仅替换第一个匹配项。

var str = "我爱北京天安门,天安门上太阳升。";
var re = /北京|天安门/g;  //  找到北京 或者天安门 全局匹配
var str2 = str.replace(re,'*'); 
alert(str2)  //我爱**,*上太阳升 

//这种只是把找到的变成了一个*,并不能几个字就对应几个*。

要想实现几个字对应几个*,我们可以用回调函数实现:

var str = "我爱北京天安门,天安门上太阳升。";
var re = /北京|天安门/g;  //  找到北京 或者天安门 全局匹配
var str2 = str.replace(re,function(str){
              alert(str); //用来测试:函数的第一个参数代表每次搜索到的符合正则的字符,所以第一次str指的是北京 第二次str是天安门 第三次str是天安门
            var result = '';
            for(var i=0;i<str.length;i++){
                result += '*';
            }              
            return result; //所以搜索到了几个字就返回几个* 
        });
alert(str2)  //我爱*****,***上太阳升         

//整个过程就是,找到北京,替换成了两个*,找到天安门替换成了3个*,找到天安门替换成3个*。

replace是一个很有用的方法,经常会用到。

: 或的意思。

语法

str.replace(regexp|substr, newSubStr|function)

参数

  • regexp (pattern)

    一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。

  • substr (pattern)

    一个将被 newSubStr 替换的 字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。

  • newSubStr (replacement)

    用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名。参考下面的使用字符串作为参数

  • function (replacement)

    一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考下面的指定一个函数作为参数

使用字符串作为参数

替换字符串可以插入下面的特殊变量名:

| 变量名 | 代表的值 | | —— | ———————————————————— | | $$ | 插入一个 “$”。 | | $& | 插入匹配的子串。 | | $ | 插入当前匹配的子串左边的内容。 | | $’ | 插入当前匹配的子串右边的内容。 | | $n | 假如第一个参数是 [RegExp`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/RegExp)对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始 |

指定一个函数作为参数

你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后,该函数就会执行。 函数的返回值作为替换字符串。 (注意:上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是,如果第一个参数是正则表达式,并且其为全局匹配模式,那么这个方法将被多次调用,每次匹配都会被调用。

下面是该函数的参数:

变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, ... 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+p2 就是匹配的 \b+
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1)
string 被匹配的原字符串。
groups 命名捕获组匹配的对象
function replacer(match, p1, p2, p3, offset, string,groups) {
  // p1 is nondigits, p2 digits, and p3 non-alphanumerics
  return [p1, p2, p3].join(' - ');
}
var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString);  // abc - 12345 - #$*%

match

match() 在字符串中搜索复合规则的内容,搜索成功就返回内容,格式为数组,失败就返回null。

例子找出指定格式的所有数字如下找到 1235433879

var str = 'haj123sdk54hask33dkhalsd879';
var re = /\d+/g;  

// 每次匹配至少一个数字  且全局匹配  如果不是全局匹配,当找到数字123,它就会停止了。就只会弹出123.加上全局匹配,就会从开始到结束一直去搜索符合规则的。如果没有加号,匹配的结果就是1,2,3,5,4,3,3,8,7,9并不是我们想要的,有了加号,每次匹配的数字就是至少一个了。

alert( str.match(re) );   // [123,54,33,879]
  1. 如果有g全局标志,那么返回的数组保存的是,所有匹配的内容,不包括子匹配
  2. 如果没有g全局标志,那么返回的数组arr.arr[0]保存的是完整的匹配.arr1保存的是第一个括号里捕获的字串,依此类推arr[n]保存的是第n个括号捕获的内容.也就是当包含有全局的标志时则返回的结果第一个是正确匹配的结果,后面依次是子匹配的结果。

exec

与match方法不同exec属于正则表达式的方法。 语法:var result1 = regexp.exec(str); regexp:正则表达式(可以直接定义也可以利用RegExp的方式定义) str:要匹配的字串

exec与match的关联就是exec(g有没有都无影响)就等价于不含有g全局标志的match.即返回数组arr[0]为匹配的完整串.其余的为括号里捕获的字符串(当含有子匹配时).

当exec和match中具有相同的子表达式且为非全局匹配时两者的输出也是相同的

var str="visit W3cschool  a W3cschool bull";
var reg=new RegExp("W3c(school)");
var b=reg.exec(str);
console.log(b);
console.log(str.match(/W3c(school)/));

执行上诉代码的结果为W3cschool,school


当为全局匹配时

var str="visit W3cschool  a W3cschool bull";
var reg=new RegExp("W3cschool",'g');
var b=reg.exec(str);
console.log(b);
console.log(str.match(/W3cschool/g));

Exec中没有子表达式其输出为W3cschool,其输出只一个,match全局匹配时其输出元素中将包含所有的匹配项,其输出为W3cshcool,W3cschool

match和exec总结

exec中不管是不是全局的匹配,只要没有子表达式,其返回的都只有一个元素的数组,如果是全局匹配,可以利用lastIndex进行下一个匹配(更好的办法是反复调用exec方法),匹配成功后reg的lastIndex的值将会变为上次匹配的字符的最后一个位置的索引。在设置g属性后,虽然匹配结果不受g的影响,返回结果仍然是一个数组(第一个值是第一个匹配到的字符串,以后的为分组匹配内容),但是会改变index和 lastIndex等的值

  var patt = new RegExp('ab', 'g');
  var str = 'abcdef12ab34cd56ef';
  var ret;
  while((ret = patt.exec(str))!=null) {
    document.write(ret+"</br>");
    document.write("ret.input="+ret.input+"</br>");
    document.write("ret.index="+ret.index+"</br>");
    document.write("RegExp.lastIndex ="+patt.lastIndex +"</br>");
}

输出结果:

ab
ret.input=abcdef12ab34cd56ef
ret.index=0
RegExp.lastIndex =2
ab
ret.input=abcdef12ab34cd56ef
ret.index=8
RegExp.lastIndex =10

这里之所以会有两次ab 是因为RegExp(‘ab’, ‘g’)

当没有全局的变量g时,由于index和lastindex的值不会变化(除非手动修改),则会导致每次的陪匹配都是从字符串的头开始的,所以只要字符串中有匹配,就会导致死循环,当时设置g后,会自动改变前面的两个属性,会依次向后匹配直到没有匹配项退出循环

Match在非全局匹配时其他几种情况下(有无子匹配的情况下)的返回结果和exec是相同的,在全局匹配时其将返回包含所有匹配项的数组(其中不包含子匹配)。

其他

命名分组

命名分组的语法是 (?<name>...),其中 <name> 是子表达式的名称。这样可以在正则表达式的其他地方通过 \k<name> 引用该子表达式的匹配结果。

const regex = /(?<content>\([^)]+\))/;
const text = "The content is (example).";
const match = text.match(regex);
console.log(match.groups); // { content: '(example)' }
console.log(match.groups.content); // '(example)'
console.log(match[0]); // '(example)'

正则中的字符

(),小括号,叫做分组符。就相当于数学里面的括号。如下: var str = ‘2013-6-7’; var re1 = /\d-+/g; // 全局匹配数字,横杠,横杠数量至少为1,匹配结果为: 3- 6- var re1 = /(\d-)+/g; // 全局匹配数字,横杠,数字和横杠整体数量至少为1 3-6- var re2 = /(\d+)(-)/g; // 全局匹配至少一个数字,匹配一个横杠 匹配结果:2013- 6-

var str = '2013-6-7';
var re = /(\d+)(-)/g;

str = str.replace(re,function($0,$1,$2){
    
       //replace()中如果有子项,
      //第一个参数:$0(匹配成功后的整体结果  2013-  6-),
         // 第二个参数 : $1(匹配成功的第一个分组,这里指的是\d   2013, 6)
        //第三个参数 : $1(匹配成功的第二个分组,这里指的是-    - - )   
    return $1 + '.';  //分别返回2013.   6.
    
});

alert( str );   //2013.6.7
//整个过程就是利用子项把2013- 6- 分别替换成了2013. 6.  最终弹出2013.6.7
match方法也会返回自己的子项如下
var str = 'abc';
var re = /(a)(b)(c)/;
alert( str.match(re) );  //[abc,a,b,c]( 返回的是匹配结果 以及每个子项  当match不加g的时候才可以获取到子项的集合)

补充:exec()方法:和match方法一样,搜索符合规则的内容,并返回内容,格式为数组。 用法:正则.exec(字符串);

 var testStr = "now test001 test002";   
 var re = /test(\d+)/; //只匹配一次     
 var r = "";   
 var r = re.exec(testStr)
  alert(r);// test001  001 返回匹配结果,以及子项
  alert(r.length); //2   返回内容的长度
  alert(r.input); //now test001 test002    代表每次匹配成功的字符串 
  alert(r[0]);   //test001   
  alert(r[1]);  //001    代表每次匹配成功字符串中的第一个子项 (\d+) 
  alert(r.index );   //  4   每次匹配成功的字符串中的第一个字符的位置

[] : 表示某个集合中的任意一个,比如 [abc] 整体代表一个字符 匹配 a b c 中的任意一个,也可以是范围,[0-9] 范围必须从小到大。 [^a] 整体代表一个字符 :^写在[]里面的话,就代表排除的意思

例子:匹配HTML标签 比如<div class="b">hahahah </div> 找出标签

var re = /<[^>]+>/g; //匹配左括号 中间至少一个非右括号的内容(因为标签里面还有属性等一些东西),然后匹配右括号
var re = /<[\w\W]+>/g; //匹配左括号 中间至少一个字符或者非字符的内容,然后匹配右括号
// 其实就是找到左括号,然后中间可以有至少一个内容,一直到找到右括号就代表是一个标签。

转义字符

\s : 空格 \S : 非空格 \d : 数字 \D : 非数字 \w : 字符 ( 字母 ,数字,下划线_ ) \W : 非字符 .(点)——任意字符 . : 真正的点 \b : 独立的部分 ( 起始,结束,空格 ) \B : 非独立的部分

var str = 'onetwo';
var str2 ="one two";

var re = /one\b/;  // e后面必须是独立的 可以是起始,空格,或结束

alert( re.test(str) ); //false
alert( re.test(str2) );//true  

找重复项最多的字符个数

var str = 'assssjdssskssalsssdkjsssdss';

var arr = str.split(''); //把字符串转换为数组
str = arr.sort().join(''); //首先进行排序,这样结果会把相同的字符放在一起,然后再转换为字符串
//alert(str);  // aaddjjkklsssssssssssssssss

 var value = '';
 var index = 0; 
var re = /(\w)\1+/g;  //匹配字符,且重复这个字符,重复次数至少一次。
str.replace(re,function($0,$1){ 
   //alert($0);   代表每次匹配成功的结果 : aa dd jj kk l sssssssssssssssss
     //alert($1);  代表每次匹配成功的第一个子项,也就是\w:  a d j k l S 
  
    if(index<$0.length){  //如果index保存的值小于$0的长度就进行下面的操作
          index = $0.length;  // 这样index一直保存的就在最大的长度
           value = $1;  //value保存的是出现最多的这个字符
    }

}); 

alert('最多的字符:'+value+',重复的次数:'+index);  // s   17

量词

量词:代表出现的次数 {n,m}:至少出现n次,最多m次 {n,} :至少n次 *:任意次 相当于{0,} ?:零次或一次 相当于{0,1} +:一次或任意次相当于 {1,} {n}: 正好n次

//^ : 放在正则的最开始位置,就代表起始的意思,注意  /[^a] /   和   /^[a]/是不一样的,前者是排除的意思,后者是代表首位。
//$ : 正则的最后位置 , 就代表结束的意思

匹配中文的正则表达式

[\\u4e00-\\u9fa5]

.字符

. 特殊字符在中括号表达式时  [.] 只会匹配 .字符等价于 \.而非匹配除换行符 \n 外的所有字符
var str = "runoob.com";
var patt1 = /[.]/;
document.write(str.match(patt1));  //输出 .

^ 和 ^指定字符串

  • ^ 指的是匹配字符串开始的位置
  • [^指定字符串] 指的是除指定字符串以外的其他字符串
(^[0-9])+     // 匹配有一至多个数字的字符串组合
[^[0-9]]+     // 匹配有一至多个不含数字的字符串组合

(?:)

https://segmentfault.com/q/1010000010302799

匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。

比较(X)和(?:X),前者是捕获分组,后者不捕获,区别在于正则表达式匹配输入字符串之后所获得的匹配的(数)组当中没有(?:X)匹配的部分;

var m = "abcabc".match(/(?:a)(b)(c)/)
//结果 ["abc", "b", "c"]
// m[0] 是/(?:a)(b)(c)/匹配到的整个字符串,这里包括了a
// m[1] 是捕获组1,即(b)匹配的子字符串substring or sub sequence
// m[2] 是捕获组2,即(c)匹配到的

?=X

”?=n” 匹配任何其后紧接指定字符串 n 的字符串

比如,你想匹配一个“人”字,但是你只想匹配中国人的人字,不想匹配法国人的人

就可以用一下表达式 (?=中国)人

(?=.*[a-z])\d+

这个就表示 匹配以“任意字符连着一个小写字母”开头的数字,只匹配数字。

而 (?<=exp)这个是放后面的。


(?:pattern)

非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在**使用或字符“( )”来组合一个模式的各个部分是很有用**。例如“industr(?:y ies)”就是一个比“industry industries”更简略的表达式。

举个例子:

/\W|^(?:value|checked|selected|muted)$/

enter description here

/\W|^value|checked|selected|muted$/

enter description here

(?=pattern)

非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如,“Windows(?=95 98 NT 2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

(?!pattern)

非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如“Windows(?!95 98 NT 2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。

(?<=pattern)

非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95 98 NT 2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。

(?<!pattern)

非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95 98 NT 2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。这个地方不正确,有问题

懒惰限定符

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

\b\B的区别

https://segmentfault.com/a/1190000013084624

\b - 一个词的边界

匹配必须出现在 \w(字母数字)和 \W(非字母数字)字符之间的边界上。

\B - 一个非单词边界

模式: `\Bend\w*\b`

原字符串: `end sends endure lender`

匹配结果: `ends``ender`

常用的正则


    // 空格
    blank: /(^\s+)|(\s+$)/g,
    // 注释
    comment: /(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm,
    // 图片
    images: /\.(jpeg|jpg|gif|png|webp)$/,
    // url
    url: /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i,
    // media
    media: /\.(jpeg|jpg|gif|png|webp|ico|mp3|mp4|oog|wav|eot|svg|ttf|woff|woff2|TTF|otf|OTF|json)$/,
    tpl: /\.(html|php|vm)$/,
    js: /\.js$/,
    css: /\.css$/,
    singleBraceInterpolate: /{([\s\S]+?)}/g,
    doubleBraceInterpolate: //g,
    htmlTag: /(<([^>]+)>)/ig,
    require: /(?:(?:var|const)\s*(.*?)\s*=\s*)?require\(['"]([^'"]+)['"](?:, ['"]([^'"]+)['"])?\);?/g,
    requireAsync: /require.async\(\[?((?:['"][^'"]+['"],?\s?)*)\]?\s?,\s?(?:function\s?\((.*)\))?/g,
    useStrict: /(\/\/.*\r?\n)*['"]use strict['"];?/g

题目

获取on后面的字符串

  let name = 'onXxx'
  let res = name.match(/^on([\s\S]+)$/)  
  //  ["onXxxxx", "Xxxxx", index: 0, input: "onXxxxx", groups: undefined]
  console.log(RegExp.$1) // Xxx

有一个很重要的知识点是RegExp.$1

一道很好的题目

说明:给定一个编码字符,按编码规则进行解码,输出字符串,编码规则是cout[letter],将letter的内容count次输出,count是0或正整数,letter是区分大小写的纯字母。

示例: const s = ‘3[a]2[bc]’ 返回aaabcbc const s = ‘3[a2[c]]’ 返回accaccacc

    function decodeString(str) {
      let reg = /(\d+)\[(\w+)\]/g
      str = str.replace(reg, (total, word1, word2) => {
        return new Array(Number(word1)).fill(word2).join('')
      })
      return reg.test(str) ? decodeString(str) : str
    }

    let str = '3[a2[c]]'
    console.log(decodeString(str))  //accaccacc

原理

正则表达式原理