正则表达式相关

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 命名捕获组匹配的对象

举个例子:

template parser

let str = '<div> </div>';
let reg = /\{\{\s*(\S+)\s*\}\}/g;

let data = {
  aaa: '我是aaa',
  bbb: '我是bbbb',
};

const res = str.replace(reg, (match, p1, offset, string, groups) => {
  if (p1) {
    return data[p1];
  }
});

console.log(111, res);

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]

match() 返回一个 数组null

  • 如果正则表达式没有找到匹配项,返回 null
  • 如果找到了匹配项,返回一个数组,数组的第一个元素是匹配的完整字符串,后面的元素是所有捕获的分组(如果有的话)。
  • 如果使用的是带有 g(全局)标志的正则表达式,match() 会返回一个数组,包含所有的匹配字符串,而不会包含分组信息。

exec

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

当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总结

全局标志(g)的影响

  • 对于带有全局标志 g 的正则表达式,match() 返回所有的匹配项(仅匹配字符串),不会返回捕获的分组信息。
  • 对于带有全局标志 g 的正则表达式,exec() 每次调用会返回一个匹配项,并且它会保存“搜索位置”,使得每次调用时返回下一个匹配项。

捕获组的差异

  • 如果没有全局标志,match()exec() 都能返回捕获组(即正则表达式中括号 () 中的内容)。
  • 如果有全局标志,match() 只返回匹配的字符串,不包含捕获组信息,而 exec() 则会返回捕获组(如果有的话)。

对于单次匹配,match() 通常更方便;对于多次匹配,特别是带 g 标志时,exec() 提供了更多的控制。

const str = 'The quick brown fox';
const regex = /(\w+)\s(\w+)/;
// 返回一致
console.log(str.match(regex));  // ["The quick","The","quick"]
console.log(regex.exec(str));  // ["The quick","The","quick"]
const str = 'The quick brown fox';
const regex = /(\w+)\s(\w+)/g;

console.log(str.match(regex)); //  ['The quick', 'brown fox']

let match = null;
while ((match = regex.exec(str)) != null) {
  console.log(match[0]); // 匹配项
  console.log(match[1]); // 匹配项中分组1
  console.log(match[2]); // 匹配项中分组2
  console.log('----------');
}

其他

命名分组

命名分组的语法是 (?<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/g
原字符串: `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

原理

正则表达式原理

正则表达式(Regular Expression,简称 regex)是一种用来描述文本模式的工具,可以在文本中进行复杂的查找、匹配、替换等操作。正则表达式的原理基于有限状态机、模式匹配和字符集等概念,下面我们将从 基本原理常见概念 两个角度来理解正则表达式的工作原理。

基本原理:有限状态机(Finite State Machine, FSM)

正则表达式的工作机制本质上是通过构建一个 有限状态机 来扫描输入的文本。有限状态机是一种数学模型,具有以下特点:

  • 状态:每个状态代表一个文本处理的阶段。
  • 转移:根据当前的字符,状态机可以从一个状态转移到另一个状态。
  • 接受状态:如果状态机扫描完输入文本并最终停留在一个特定的接受状态,就表示匹配成功。

简言之,正则表达式通过不断地扫描输入的字符,并根据字符和当前状态的规则进行状态转移。如果扫描完输入字符串后,状态机最终停留在一个接受状态,则表示输入字符串符合正则表达式的模式。