首页 > 技术文章 > JS正则表达式

shige720 2019-08-11 16:58 原文

定义

  正则表达式可以理解成匹配符合要求的字符串的表达式,由普通字符和特殊字符构成,用来查找替换文本

创建

字面量(直接量)

  在一对反斜线中写正则表达式内容,如/js/

  一般多使用字面量方式,但字面量把正则“写死了”,不能改变

构造函数

  构造正则表达式的实例,如new RexExp('js')

  内部传入的参数为字符串/字符串的变量

  如果需要根据用户输入来改变正则表达式,则用构造函数的方法创建

 

字符分类

普通字符

  字母、数字、下划线、汉字、没有特殊含义的符号(,;!@等)

  实际上不是特殊字符的字符都是普通字符

特殊字符

  \:将特殊字符转义成普通字符

模式修饰符

  i:ignoreCase,匹配时忽视大小写

  m:multiline,多行匹配

  g:global,全局匹配

  字面量创建正则时,模式修饰符写在一对反斜线后

1 var find = /js/i;

   构造对象创建正则时,模式修饰符作为第二个参数,传递给RexExp对象

1 var find = new RegExp('js','i');

 

字符匹配方式

字符类

  用[]括起来,会将待匹配字符从最开头到最后挨个找,看是否有字符类中的字符,如果有,返回这个字符,否则返回null

  用^表示取反,即除了字符类中的字符,其余字符只要出现就返回这个字符

  用-表示范围,即只要字符出现在字符类的范围里,就返回该字符

  表示范围时,范围的字符的ASCII码必须小于等于范围后面字符,且不可跨越大小写、数字范围,不同范围之间紧密相连即可

  

 常用字符类

  .:可取到除了换行符(\n)以外的所有字符

  \w:包含大小写字符、数字以及下划线

  \W:包含除了大小写字符、数字以及下划线之外的字符(即\w取反

  \d:包含0-9的数字

  \D:包含除了0-9数字之外的字符(即\d取反

  \s:包含空格、制表符、unicode里的多个空格符

  \S:除了空格、制表符、unicode里多个空格符之外的字符(即\s取反

  以上常用字符类无需写到[]里,但也都可以写到[]中,写入多个时,只能匹配其中一类(例如\d\s只能按目标字符串顺序,匹配数字或空格)

重复

  用添加在{}内的数字并放在字符类后,用以控制匹配字符类的次数

  其中的数字可以写两个,逗号分隔(注意逗号和数字之间不要有空格),前面的数表示下限,后面的表示上限

  下限必须小于等于上限,下限不可省略,上限可省略

  

数量的简写

  ?:表示匹配0/1次

  +:表示至少匹配1次

  *:表示可匹配0次及以上

  

 非贪婪匹配

  正则表达式默认贪婪匹配,例如用+规定可以匹配一个以上的字符,正则会尽可能多地去匹配而不是取下限匹配一次

  在量词({}或简写符号)后加?,就会在条件允许下尽可能少去匹配

  (用课里说到的一个案例,说明只是尽可能去不贪婪) 

选择

  用|,表示匹配时选择其中之一即可,匹配到其中一个就输出

  实际匹配时,会按照字符串中的书写顺序,从左往右找,跟正则中的选项从左向右查找,如果匹配上其中一个,就输出

  

分组

  用()把字符类括起来,用来对整个字符类进行数量的修饰等操作,同时匹配时也会返回分组中字符类的查找结果

  当不需要返回分组中的内容时(不捕获分组中的内容),在分组的内容前加上?:即可

  

  多个括号嵌套时,从左到右以左半括号的位置,分别为第一个、第二个、第三个分组

  

引用

  对用()括起来的字符类,可以在正则表达式中用\序号的形式直接引用,其中序号表示该分组号(即是第几个分组

  引用时,该分组必须设置为捕获(即分组前不可有?:),否则返回null

  

  引用让后面匹配到和前面一样的内容(例如忽略大小写时保持前后匹配的大小写一致,匹配到同一个标签等)

  

 

位置匹配方式

首匹配

  让要匹配的正则中的字符必须在原字符串中的开头

  在正则表达式最开始加^(此处的^同字符类中的不同,字符类中表示取反

尾匹配

  让要匹配的正则中的字符必须在原字符串中的结尾

  在正则表达式最后加$

单词边界匹配

  用\b来实现(\b表示\w和\W之间的位置,即单词的开头和之前的部分的位置、单词的结尾和结尾后的部分之间的位置)

前瞻性匹配/负向前瞻性匹配

  用来匹配字符串后为/不为指定字符串内容的字符串(例如匹配上java后为/不为script的)

  前瞻性匹配需要在匹配的内容前加上?=,负向前瞻性则加上?!

  

应用

  可以验证输入内容都是数字(例如用在QQ号全是数字的密码等)

1 var find = /^\d+$/;//此处表示首尾之间全部是数字,且数字至少为1个
首位匹配

  也可查找指定的class名称(即使class前后有多个空格)

 1 function getByClassName(className,parentNode){
 2     //如果有getElementsByClassName方法,直接调用
 3     if(!document.getElementByClassName){
 4         return document.getElementsByClassName(className);
 5     }
 6     else{
 7         //当没有传入parentNode时,自动赋值为document
 8         parentNode = parentNode || document;
 9         var nodeList = [];
10         //挑选出parentNode下的所有标签
11         var allNodes = parentNode.getElementsByTagName('*');
12         //此处用正则表示匹配上classname前后有多个空格以及在首尾的情况
13         var pattern = new RegExp('(^|\\s+)'+className+'($|\\s+)');
14         //用循环遍历,找出其中可以用正则匹配出的指定class名称的标签
15         //将这些标签装到nodeList数组中
16         for(var i=0;i<allNodes.length;i++){
17             if(pattern.test(allNodes[i].className)){
18                 nodeList.push(allNodes[i]);
19             }
20         }
21         //完成装入,返回nodeList数组
22         return nodeList;
23     }
24 }
class查找(首尾匹配)
 1 function getByClassName(className,parentNode){
 2     //如果有getElementsByClassName方法,直接调用
 3     if(!document.getElementByClassName){
 4         return document.getElementsByClassName(className);
 5     }
 6     else{
 7         //当没有传入parentNode时,自动赋值为document
 8         parentNode = parentNode || document;
 9         var nodeList = [];
10         //挑选出parentNode下的所有标签
11         var allNodes = parentNode.getElementsByTagName('*');
12         //此处用正则表示匹配上classname为一个单独单词的情况
13         var pattern = new RegExp('\\b'+className+'\\b');
14         //用循环遍历,找出其中可以用正则匹配出的指定class名称的标签
15         //将这些标签装到nodeList数组中
16         for(var i=0;i<allNodes.length;i++){
17             if(pattern.test(allNodes[i].className)){
18                 nodeList.push(allNodes[i]);
19             }
20         }
21         //完成装入,返回nodeList数组
22         return nodeList;
23     }
24 }
class查找(单词边界)

 

正则实例方法

test

  用来测试待检测的字符串中是否有可以匹配到正则表达式的字符串

  如果有返回true,否则返回false

  

exec

  可用来匹配字符串中符合正则表达式的字符串

  如果匹配到,返回该字符串的数组(注意这个字符串本身是不带有引号的,也就是你让它匹配的文字内容),否则返回null

  没写全局匹配时,只会匹配原字符串中第一个符合正则要求的字符串

  添加分组时,返回符合正则表达式的内容以及分组内容,开启全局匹配不改变该结果

  

  返回的数组中有以下属性:

  第一个属性(0)为匹配上的字符串

  index为该字符串第一个字母的起始位置

  input为被匹配数组的全部内容

  

 

toString/toLocaleString

  把正则表达式的内容转化成字面量形式字符串/有本地特色的字符串(JS中没效果

valueOf

  返回正则表达式本身

  

正则实例属性

lastIndex

  当没设置全局匹配时,该属性值始终为0

  设置了全局匹配时,每执行一次exec/test来匹配,latIndex就会移向匹配到的字符串的下一个位置,当指向的位置后没有可以再次匹配的字符串时,下一次执行exec返回null,test执行返回false,然后lastIndex归零,从字符串的开头重新匹配一轮

  可以理解成,每次正则查找的起点就是lastIndex

  

ignoreCase、global、multiline

  判断正则表达式中是否有忽略大小写、全局匹配、多行匹配三个模式修饰符

  

$1-9

  返回正则中的分组(最多用9个分组),但必须先执行exec或test方法

  

source

  返回字面量形式的正则表达式(类似于toString

  

flags

  返回正则表达式的模式匹配符

input($_)

  返回正则查找的原字符串,但如果没有执行过exec或test方法,则返回undefined

lastmatch($&)(不常用)

  返回最近一次匹配到的字符

left(right)Context($`或$')(不常用)

  返回上次匹配左/右剩余的字符

lastParen(不常用)

  返回上次通过分组捕获的子选项

 

String对象中和正则相关的方法

search

  查找字符串中是否有匹配正则的字符串,有则返回字符串第一次出现时的位置,无则返回null

  正则中无论是否有全局匹配都不会影响返回结果

  

match

  匹配字符串中符合正则表达式的字符串,并返回该字符串的一个数组,其中包括字符串内容位置

  如果正则设置全局匹配,则一次性返回所有符合正则表达式的字符串数组

  如果其中添加了分组,返回符合要求的字符串以及分组的一个数组,但如果同时开启全局匹配则不会在数组中添加分组内容

  

  match中的正则添加了多行匹配,只有在同时添加全局匹配以及首位匹配时才起作用(例如匹配全局中每行行首的指定字符)

  只添加多行匹配时同普通匹配一致,只添加多行匹配和全局匹配也和全局匹配模式一致

  

split

  分隔字符串时,传入的参数可以是字符串(,等),也可以是正则表达式

  

replace

  替换字符串时,参数可以传递字符串,也可为正则表达式

  可以在过滤敏感词时使用(见下方案例)

1 var str = '我草草地去吃了一顿草草的饭';
2 var find = /草/g;//设置全局匹配以屏蔽所有敏感词
3 console.log(str.replace(find,function($0){
4     var result = '';
5     for(var i=0;i<$0.length;i++){//获取其中敏感词的长度,添加不同数量的*
6         result += '*';
7     }
8     return result;
9 }));
replace过滤敏感词

 

常用的正则表达式

  在写正则时,需要先明确规则,然后按照规则逐条编写

  建议可以把常用的正则用一个对象来包括起来,用属性名表示匹配啥,属性值表示正则

QQ号

  全是数字,首位不可为0,最少5位数(目前最大11位数,可以为了未来考虑不设置上限)

1 //此处首位匹配,首位不是0,且都是数字,不少于5位
2 var findQQ = /^[1-9]\d{4,}$/;
QQ号的正则

昵称(以慕课网为例)

  2-18位,中英文、数字及下划线

1 //为了匹配昵称,添加首尾匹配
2 var findname = /^[\u4e00-\u9fa5a-zA-Z0-9_{2,18}]$/;
3 //可以把英文、数字、下划线统一用\w表示
4 var findname2 = /^[\u4e00-\u9fa5\w]{2,18}$/;
昵称的正则

密码(以慕课网为例)

  6-16位,不可用空格(空白字符),区分大小写

1 //\s表示空白字符(包括空格、制表符、换行等),\S则取反
2 var findpassword = /^\S[6,16}$/;
3 // 也可用穷举
4 var findpassword2 = /^[\w~#!@$%\[\]]{6,16}$/;
密码的正则

去除字符串首尾的空白字符

  正则也跟函数类似,不必追求用一个正则表示全部的功能,可以考虑用多个正则以增强可读性

  这里也可以考虑用函数来封装正则表达式,实现去除空白的效果

1 //可以同时首尾替换,设置全局匹配
2 var deletespace = /^\s+|\s+$/g;
去除空白1
1 //也可用两个正则,一个用来匹配前面的空格,一个用来匹配后面的
2 var pattern1 = /^\s+/;
3 var pattern2 = /\s+$/;
4 console.log(str.replace(pattern1,'').replace(pattern2,''));
去除空白2

转驼峰

  把连字符后的字母变成大写

1 //全局匹配所有连字符,然后找到其后面的字母,在函数中返回其大写形式
2 //考虑到要返回该字母,需要对字母进行一个分组
3 var pattern = /-([a-z])/gi;
4 console.log(str.replace(pattern,function(all,letter){
5     return letter.toUpperCase();
6 }));
转驼峰

匹配HTML标签

  只是匹配HTML标签,包括开始和闭合标签,标签中的属性名和属性值

1 //先顺向思考,匹配标签名(可能有反斜杠),再匹配其中属性名和属性值
2 var tocamel = /<\/?[a-zA-Z]+(\s+[a-zA-Z]+=".*")*>/g;
HTML标签1
1 //反向思考,一个标签中不可能有>,因此排除即可
2 var findHTML = /<[^>]+>/g;
HTML标签2

email邮箱

  @前可以是一个类似用户名的结构(字母数字下划线),也可包括.com,@后是一个至少有一个.的结构

  对于这些复杂的结构,可以先分组,即搭建正则的框架,然后再去书写详细内容

1 //先搭建框架,然后最后看哪些分组不是必须的就去掉
2 var findemail = /(\w+\.)*(\w+)@(\w+\.)+([a-z])/i;
3 //拆除不必要的框架后如下
4 var findemail2 = /(?:\w+\.)*\w+@(?:\w+\.)+[a-z]/i;
邮箱1
1 //改变分组模式,可以理解成@前后都是一堆字母数字后加.组成的,@后的最后是字母结尾
2 var findemail3 = /^([a-z0-9]+)([_-.][a-z0-9]+)*@([a-z0-9]+)([_-.][a-z0-9]+)*\.([a-z]{2,4})$/i;
3 //拆去不需要的分组,并把不需要捕获的添加不捕获
4 var findemail4 = /^[a-z0-9]+(?:[_-.][a-z0-9]+)*@[a-z0-9]+(?:[_-.][a-z0-9]+)*\.[a-z]{2,4}$/i;
邮箱2

URL地址

  由协议:\\(可省略)主机名(:端口号,一般省略)/路径几个部分组成

1 //先只考虑http和https这两个协议
2 var findurl1 = /^(https?:\/\/)?([^:\/]+)(:\d+)?(\/.*)?$/;
URL1

  主机名实际上是多个以.分隔开的字符串组成的,其中可以有数字、字母、下划线、连字符等,但是不可以以连字符开头/结尾

1 //只针对主机名来写一个正则
2 var hostnamefind = /[a-z0-9]|[a-z0-9][-a-z0-9]*[a-z0-9]/i;
3 //让主机名匹配详细,最后的部分可用穷举
4 var hostnamefind2 = /^([a-z0-9]\.|[a-z0-9][-a-z0-9]*[a-z0-9]\.)*(com|edu|gov|net|org|[a-z]{2}+)$/;
5 //不穷举,可简单把最后的com等用若干个英文字母代替
6 var hostnamefind3 = /^([a-z0-9]\.|[a-z0-9][-a-z0-9]*[a-z0-9]\.)*([a-z]+)$/
主机名

 

推荐阅读