js中正则使用

正则介绍

正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式,可用于文本搜索和文本替换。

语法为 /正则表达式主体/修饰符(可选) 比如 var patt = /runoob/i

基础知识

特殊字符

正则表达式中有一些字符含义比较特殊,或者某个单词加上\会变成特殊的含义,以下列举出一些

正则表达式 匹配内容 说明
\n 换行符 new line
\f 换页符 form feed
\r 回车符 return
\s 空白符 space
\t 制表符 tab
\v 垂直制表符 vertical tab
[\b] 回退符 backspace,之所以使用[]符号是避免和\b重复
/ / 因为js中使用/包裹正则表达式,所以先匹配/就需要加转义符\/
\d 单个数字, [0-9] digit
\D 除了[0-9] Not digit
\w 包括下划线在内的单个字符,[A-Za-z0-9_] word
\W 非单字字符,[^a-zA-Z_] not word
\s 匹配空白字符,包括空格、制表符、换页符和换行符,[\n\f\r\t\v] space
\S 匹配非空白字符,[^\n\f\r\t\v] not space
. 除了换行符之外的任何字符
^ 匹配字符串的开始
$ 匹配字符串的结束
\b 单词边界 boundary
\B 非单词边界 not boundary

重复匹配

重复匹配一些内容时可以使用重复匹配修饰符

修饰符 匹配方式
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
重复n次
重复n次或更多次
重复n到m次
重复n到m次
| 表示或者,两边表达式任一满足即可

懒惰模式和独占模式

懒惰模式

正则表达式在进行重复匹配时,默认是贪婪匹配模式,也就是说会尽量匹配更多内容,但是有的时候我们并不希望他匹配更多内容,这时可以通过 ? 进行修饰来禁止重复匹配

let name = "haaaaaaa";

// * 匹配零个或多个,加上问号表示匹配0个
// 匹配结果 ["h"]
let reg = /ha*?/g;
console.log(name.match(reg));

// + 匹配一个或多个,这里只匹配1个
// 匹配结果 ["ha"]
reg = /ha+?/g;
console.log(name.match(reg)); // ["so"]

// ? 匹配0个或者1个,这里只匹配0个
// 匹配结果 ["h"]
reg = /ha??/g;
console.log(name.match(reg)); // ["s"]

// {2,5} 匹配2到5个,这里只匹配2个
// 匹配结果 ["h"]
reg = /ha{2,5}?/g;
console.log(name.match(reg)); // ["soo"]
独占模式

如果在表达式后加上一个加号(+),则会开启独占模式。同贪婪模式一样,独占模式一样会匹配最长。不过在独占模式下,正则表达式尽可能长地去匹配字符串,一旦匹配不成功就会结束匹配而不会回溯。

这里所说的匹配涉及到正则表达式引擎内容,这里不做特殊说明,请想要知道详情的小伙伴先自行百度。

三种模式示例
贪婪 懒惰 独占
X? X?? X?+
X* X*? X*+
X+ X+? X++
X X{n}? X{n}+
X X{n,}? X{n,}+
X X{n,m}? X{n,m}+

模式修饰符

正则表达式有默认的执行方式,如果有一些其他匹配规则需求,可以使用模式修正符添加匹配规则。

修饰符 说明
i 不区分大小写字母的匹配
g 全局搜索所有匹配内容
m 多行匹配
s 视为单行忽略换行符,使用. 可以匹配所有字符
y 从 regexp.lastIndex 开始匹配,表示匹配到不符合的就停掉,不会继续往后匹配,必须连续的符合条件的
u 正确处理四个字符的 UTF-16 编码

高级用法

分组匹配

所有以 () 包裹的表达式都是一个分组,每个分组是一个子表达式

分组匹配时没有添加 g 模式修正符时只匹配到第一个

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /\[(App.*?)].*\[(App.*?)]/;
let result = s.match(pattern);
if (result) {
    console.log(`匹配到的完整内容:${result[0]}`);
    console.log(`第一个分组:${result[1]}`);
    console.log(`第二个分组:${result[2]}`);
    console.log(`具名分组:${result.groups}`);
    console.log(`在原始字段的索引位置:${result.index}`);
    console.log(`原始字段:${result.input}`);
} else {
    console.log('没有任何匹配');
}

最终结果如下:

匹配到的完整内容:[AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]
第一个分组:AppComponent
第二个分组:AppComponent
具名分组:undefined
在原始字段的索引位置:25
原始字段:@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})

回溯引用

所谓回溯引用(backreference)指的是模式的后面部分引用前面的子字符串。

你可以把它想象成是变量,回溯引用的语法像\1,\2,....,其中\1表示引用的第一个子表达式,\2表示引用的第二个子表达式,以此类推。而\0则表示整个表达式。

举例如下,这里只不过是把第二个分组的表达式使用\1进行表示,匹配结果和上面的是相同的:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /\[(App.*?)].*\[(\1)]/;
let result = s.match(pattern);
if (result) {
    console.log(result);
    console.log(`匹配到的完整内容:${result[0]}`);
    console.log(`第一个分组:${result[1]}`);
    console.log(`第二个分组:${result[2]}`);
    console.log(`具名分组:${result.groups}`);
    console.log(`在原始字段的索引位置:${result.index}`);
    console.log(`原始字段:${result.input}`);
} else {
    console.log('没有任何匹配');
}

回溯引用在替换字符串中十分常用,语法上有些许区别,用$1,$2...来引用要被替换的字符串。

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

变量 说明
$$ 插入一个 "$"
$& 插入匹配的子串
$` 使用 $` 插入当前匹配的子串左边的内容
$' 使用 $' 插入当前匹配的子串右边的内容
$n 假如第一个参数是 RegExp 对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /\[App(.*?)]/g;
console.log('替换固定单词 => ', s.replace(pattern, 'Base$1'));

s = '-center=';
pattern = /center/g;
console.log('增加左右字符数量 => ', s.replace(pattern, "$`$`$`$&$'$'$'"));

s = '(010)12345678 (0246)7654321';
pattern = /\((\d{3,4})\)(\d{7,8})/g;
console.log('电话替换为 - 连接 => ', s.replace(pattern, '$1-$2'));

嵌套分组和不记录分组

嵌套分组就是有很多的 /(1(2(3)))/ 回溯引用时的状态,如 /(1(2(3)))-\1\2\3/

如果我们不想子表达式被引用,可以使用非捕获正则(?:regex)这样就可以避免浪费内存,分组里面加上 ?: 表示不记录该分组,但是分组的功能仍然生效,比如 let s = 'haaaaa';s.replace(/(ha)(?:a)/, '$1,$2') 只会返回 ha,$2

分组具名

匹配的分组也可以起一个名字,结果将保存在返回的 groups 字段中

  • 使用 ? 给分组起别名
  • 使用 $ 读取别名

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /\[(?<componentName>App.*)]/;
let result = s.match(pattern);
if (result) {
    // 注意:加 g 模式修饰符返回结果类型为 Array ,没有 groups 属性
    console.log(result.groups);
} else {
    console.log('没有任何匹配');
}

最终结果如下:

{componentName: 'AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent'}

断言匹配

有时,我们需要限制回溯引用的适用范围,通过断言匹配就可以达到这个目的。

断言虽然写在括号中但它不是分组,所以不会在匹配结果中保存,可以将断言理解为正则中的条件。

断言用来声明一个应该为真的事实,正则表达式中只有当断言为真时才会继续进行匹配。

零宽先行断言

正向肯定查找,用来检查接下来出现的是不是某个特定的字符集,语法为 (?=exp)

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /\[AppComponent](?=})/;
let result = s.match(pattern);
if (result) {
    console.log(result.index);
} else {
    console.log('没有任何匹配');
}

最终结果如下:

在原始字段的索引位置:90

可以看到只匹配到了第二个

零宽后行断言

反向肯定查找,用来检查前面出现的是不是某个特定的字符集,语法为 (?<=exp)

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /(?<=p:\s)\[AppComponent]/;
let result = s.match(pattern);
if (result) {
    console.log(result.index);
} else {
    console.log('没有任何匹配');
}

最终结果如下:

在原始字段的索引位置:90

可以看到只匹配到了第二个

零宽负向先行断言

正向否定查找,用来检查接下来不应该出现匹配内容的字符集,语法为 (?!exp)

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /\[AppComponent](?!,)/;
let result = s.match(pattern);
if (result) {
    console.log(result.index);
} else {
    console.log('没有任何匹配');
}

最终结果如下:

在原始字段的索引位置:90

可以看到只匹配到了第二个

零宽负向后行断言

反向否定查找,用来检查前面不应该出现的字符集,语法为 (?<!exp)

举例如下:

let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
let pattern = /(?<!s:\s)\[AppComponent]/;
let result = s.match(pattern);
if (result) {
    console.log(result.index);
} else {
    console.log('没有任何匹配');
}

最终结果如下:

在原始字段的索引位置:90

可以看到只匹配到了第二个

正则可视化及测试工具

以下网站均可实现一定的正则可视化

以下网站可以进行正则表达式的测试

js 使用正则表达式

变量转化为正则表达式

我就是为了这个醋包了这顿饺子

js中的正则表达式有两种创建RegExp对象方法

  1. 字面量方式

    字面量方式,由包含在斜杠之间的模式组成,如/abc/,脚本加载后,正则表达式字面量就会被编译。当正则表达式保持不变时,使用此方法可获得更好的性能

  2. RegExp对象的构造函数

    我们正则表达式中可能有些内容需要通过变量进行动态替换,那么怎么把变量转换为正则表达式呢。可以直接使用构造函数 new RegExp 创建正则表达式。

    let s = '@NgModule({declarations: [AppComponent], imports: [CoreModule], providers: [], bootstrap: [AppComponent]})';
    let componentName = 'AppComponent';
    let regExpString = `(?<!s:\\s)\\[${componentName}]`;
    let pattern = new RegExp(regExpString);
    let result = s.match(pattern);
    if (result) {
        console.log(`在原始字段的索引位置:${result.index}`);
    } else {
        console.log('没有任何匹配');
    }
    

    最终结果如下:

    在原始字段的索引位置:90
    

常用方法

  • RegExp对象

    • exec 需要捕获结果时使用
    • test 只是为了判断是否匹配时使用
  • String对象

    • match
    • replace

具体的使用方法略,感兴趣的小伙伴请自行百度了

posted @ 2023-03-09 20:17  cos1eqlg0  阅读(111)  评论(0编辑  收藏  举报