1 认识正则表达式
1.1 什么是正则表达式
正则表达式(Regular Expression,简称regexp)是一种描述字符串结构的语法规则。
是一个特定的格式化模式,用于验证各种字符串是否匹配这个特征,进而实现高级的文本查找、替换、截取内容等操作。
在项目开发中,手机号码指定位数的隐藏、数据采集、敏感词的过滤以及表单的验证等功能,都可以利用正则表达式来实现。
适用领域:在操作系统(Unix、Linux等)、编程语言(C、C++、Java、PHP、Python、JavaScript等)。
比如,若在大量的文本中找出符合某个特征的字符串(如手机号码),就将这个特征按照正则表达式的语法写出来,形成一个计算机程序识别的模式(Pattern),然后计算机程序就会根据这个模式到文本中进行匹配,找出符合规则的字符串。
正则表达式的表现形式
- 一种是POSIX规范兼容的正则表达式,用于确保操作系统之间的可移植性。
- 一种是当Perl(一种功能丰富的编程语言)发展起来后,衍生出来了Perl正则表达式,JavaScript中的正则语法就是基于Perl的。
1.2 如何使用正则
在开发中,经常需要根据正则匹配模式完成对指定字符串的搜索和匹配。
- RegExp对象提供的exec()方法
- String对象提供的match()方法
exec()方法
exec()方法用于在目标字符串中搜索匹配,一次仅返回一个匹配结果。
例如,在指定字符串str中搜索abc。
var str = 'AbC123abc456';
var reg = /abc/i;// 定义正则对象
reg.exec(str); // 匹配结果: ["AbC", index: 0, input: "AbC123abc456"]
- “/abc/i”中的“/”是正则表达式的定界符,“abc”表示正则表达式的模式文本,“I”是模式修饰标识符,表示在str中忽略大小写。
- exec()方法的参数是待匹配的字符串str,匹配成功时,该方法的返回值是一个数组,否则返回null。
- 从exec()的返回结果中可以看出,该数组保存的第1个元素(AbC)表示匹配到的字符串;第2个元素index表示匹配到的字符位于目标字符串中的索引值(从0开始计算);第3个参数input表示目标字符串(AbC123abc456)。
match()方法
String对象中的match()方法除了可在字符串内检索指定的值外,还可以在目标字符串中根据正则匹配出所有符合要求的内容,匹配成功后将其保存到数组中,匹配失败则返回false。
var str = "It's is the shorthand of it is";
var reg1 = /it/gi;
str.match(reg1); // 匹配结果:(2) ["It", "it"]
var reg2 = /^it/gi;
str.match(reg2); // 匹配结果:["It"]
var reg3 = /s/gi;
str.match(reg3); // 匹配结果:(4) ["s", "s", "s", "s"]
var reg4 = /s$/gi;
str.match(reg4); // 匹配结果:["s"]
- 定位符“^”,可用于匹配字符串开始的位置。
- 定位符“$”,可用于匹配字符串结尾的位置。
- g表示全局匹配,用于在找到第一个匹配之后仍然继续查找。
1.3 获取正则对象
在JavaScript应用中,使用正则表达式之前首先需要创建正则对象。除了前面讲解过的字面量方式创建外,还可以通过RegExp对象的构造函数的方式创建。
// 字面量方式
// /pattern/flags
// RegExp对象构造函数方式
// new RegExp(pattern, [flags])
// RegExp(pattern, [flags])
- pattern是由元字符和文本字符组成的正则表达式模式文本。
- 元字符是具有特殊含义的字符,如“^”、“.”或“*”等。
- 文本字符就是普通的文本,如字母和数字等。
- flags表示模式修饰标识符,用于进一步对正则表达式进行设置。
模式符 | 说明 |
---|---|
g | 用于在目标字符串中实现全局匹配 |
i | 忽略大小写 |
m | 实现多行匹配 |
u | 以Unicode编码执行正则表达式 |
y | 粘性匹配,仅匹配目标字符串中此正则表达式的lastIndex属性指示的索引 |
- 模式修饰符,还可以根据实际需求多个组合在一起使用。
- 例如,既要忽视大小写又要进行全局匹配,则可以直接使用gi,并且在编写多个模式修饰符时没有顺序要求。
- 因此,模式修饰符的合理使用,可使正则表达式变得更加简洁、直观。
var str = '^abc\\1.23*edf$';
var reg1 = /\.|\$|\*|\^|\\/gi; // 字面量方式创建正则对象
var reg2 = RegExp('\\.|\\$|\\*|\\^|\\\\', 'gi'); // 构造函数方式创建正则对象
str.match(reg1); // 匹配结果:(5) ["^", "\", ".", "*", "$"]
str.match(reg2); // 匹配结果:(5) ["^", "\", ".", "*", "$"]
- 选择符“|”表示“或”,查找条件只要其中一个条件满足即可成立。
- JavaScript中字符串存在转义问题,因此代码中str里的“\”表示反斜线“\”。
- 在正则中匹配特殊字符时,也需要反斜线(\)对特殊字符进行转义。例如,“\\”经过字符串转义后变成“\”,然后正则表达式再用“\”去匹配“\”。
构造函数方式与字面量方式创建的正则对象,虽然在功能上完全一致,但它们在语法实现上有一定的区别,前者的pattern在使用时需要对反斜杠(\)进行转义。而后者的pattern在编写时,要放在定界符“/”内,flags标记则放在结尾定界符之外
2 字符类别与集合
2.1 字符类别
有效的使用字符类别可以使正则表达式更加简洁,便于阅读。
比如,大写字母、小写字母和数字可以使用“\w”直接表示。
比如,若要匹配0到9之间的数字可以使用“\d”表示。
字符 | 含义 | 字符 | 含义 |
---|---|---|---|
. | 匹配除“\n”外的任何单个字符 | \f | 匹配一个换页符(form-feed) |
\d | 匹配任意一个阿拉伯数字(0~9) | \D | 匹配任意一个非阿拉伯数字字符 |
\s | 匹配一个空白符,包括空格、制表符、换页符、换行符等 | \S | 匹配一个非空白符 |
\w | 匹配任意一个字母(大小写)、数字和下划线 | \W | 匹配任意一个非“字母(大小写)、数字和下划线”的字符 |
\b | 匹配单词分界符。如“\bg”可以匹配“best grade”,结果为“g” | \B | 非单词分界符。如“\Bade”可以匹配“best grade”,结果为“ade” |
\t | 匹配一个水平制表符(tab) | \r | 匹配一个回车符(carriage return) |
\n | 匹配一个换行符(linefeed) | \v | 匹配一个垂直制表符(vertical tab) |
\xhh | 匹配ISO-8859-1值为hh(2个16进制数字)的字符,如“\x61”表示“a” | \uhhhh | 匹配Unicode 值为 hhhh (4个16进制数字)的字符,如“\u597d”表示“好” |
var str = 'good idea';
// 正则对象
var reg = /\s../gi;
// 匹配结果:[" id"]
str.match(reg);
- 正则对象reg用于匹配空白符后的任意两个字符(除换行外)。
- 因此在控制台查看到的结果中,id前有一个空格。
2.2 字符集合
字符集合的表示方式:“[]”可以实现一个字符集合。
字符范围:与连字符“-”一起使用时,表示匹配指定范围内的字符。
反义字符:元字符“^”与“[]”一起使用时,称为反义字符。
不在某范围内: “^”与“[]”一起使用,表示匹配不在指定字符范围内的字符。
pattern | 说明 | 匹配结果 |
---|---|---|
[cat] | 匹配字符集合中的任意一个字符c、a、t | ["t"] |
[^cat] | 匹配除c、a、t以外的字符 | (6) ["g", "e", "好", "T", "B", "6"] |
[B-Z] | 匹配字母B~Z范围内的字符 | (2) ["T", "B"] |
[^a-z] | 匹配字母a~z范围外的字符 | (4) ["好", "T", "B", "6"] |
[a-zA-Z0-9] | 匹配大小写字母和0~9范围内的字符 | (6) ["g", "e", "t", "T", "B", "6"] |
[\u4e00-\u9fa5] | 匹配任意一个中文字符 | ["好"] |
字符“-”在通常情况下只表示一个普通字符,只有在表示字符范围时才作为元字符来使用。“-”连字符表示的范围遵循字符编码的顺序,如“a-Z”、“z-a”、“a-9”都是不合法的范围。
2.3 练习作业
- 限定输入内容
- 编写HTML,设置一个年(份)和月(份)的文本框,以及一个查询按钮。
- 获取操作的元素对象,对表单的提交进行验证。
- 验证年份, 正则:/^\d{4}$/。验证月份,正则: /^((0?[1-9])|(1[012]))$/ 。
- 文本框获取焦点,去除提示框的颜色。文本框失去焦点,去除输入内容的两端的空白,并进行验证。
3 字符限定与分组
3.1 字符限定
限定符
匹配一个连续出现的字符,如 6个连续出现的数字“458925”。
解决方案1:正则对象/\d\d\d\d\d\d/gi。
存在的问题:重复出现的“\d”既不便于阅读,书写又繁琐。
解决方案2:使用限定符(?、+、*、{ })完成某个字符连续出现的匹配。正则对象/\d{6}/gi。
字符 | 说明 | 示例 | 结果 |
---|---|---|---|
? | 匹配前面的字符零次或一次 | hi?t | 可匹配ht和hit |
+ | 匹配前面的字符一次或多次 | bre+ad | 可匹配范围从bread到bre…ad |
* | 匹配前面的字符零次或多次 | ro*se | 可匹配范围从rse到ro…se |
{n} | 匹配前面的字符n次 | hit{2}er | 只能匹配hitter |
{n,} | 匹配前面的字符最少n次 | hit{2,}er | 可匹配范围从hitter到hitt…er |
{n,m} | 匹配前面的字符最少n次,最多m次 | fe{0,2}l | 可匹配fl、fel和feel三种情况 |
贪婪与懒惰匹配
当点字符(.)和限定符连用时,可以实现匹配指定数量范围的任意字符。
比如,“^hello.*world$”。可匹配从hello开始到world结束,中间包含零个或多个任意字符的字符串。
正则在实现指定数量范围的任意字符匹配时,支持贪婪匹配和惰性匹配两种方式。
- 所谓贪婪表示匹配尽可能多的字符,而惰性表示匹配尽可能少的字符。在默认情况下,是贪婪匹配。
- 若想要实现惰性匹配,需在上一个限定符的后面加上“?”符号。
ar str = 'webWEBWebwEb';
var reg1 = /w.*b/gi; // 贪婪匹配
var reg2 = /w.*?b/gi; // 懒惰匹配
// 输出结果为:["webWEBWebwEb", index: 0, input: "webWEBWebwEb"]
console.log(reg1.exec(str));
// 输出结果为:["web", index: 0, input: "webWEBWebwEb"]
console.log(reg2.exec(str));
3.2 括号字符
在正则表达式中,被括号字符“()”括起来的内容,称之为“子表达式”。
作用
- 改变限定符的作用范围
// 改变作用范围前
// 正则表达式:catch|er
// 可匹配的结果:catch、er
// 改变作用范围后
// 正则表达式:cat(ch|er)
// 可匹配的结果:catch、cater
小括号实现了匹配catch和cater,而如果不使用小括号,则变成了catch和er
- 分组
// 分组前
// 正则表达式:abc{2}
// 可匹配的结果:abcc
// 分组后
// 正则表达式: a(bc){2}
// 可匹配的结果:abcbc
未分组时,表示匹配2个c字符;而分组后,表示匹配2个“bc”字符串。
捕获与非捕获
捕获:将子表达式匹配到的内容存储到系统的缓存区的过程。
非捕获:不将子表达式的匹配内容存放到系统的缓存中,使用(?:x)实现。
// 查看捕获内容
var res = '1234'.match(/(\d)(\d)(\d)(\d)/);
console.log(res);
String对象的replace()方法,可直接利用$n(n是大于0的正整数)获取捕获内容,完成对子表达式捕获的内容进行替换的操作。
// 获取捕获内容
var str = 'Regular Capture';
var reg = /(\w+)\s(\w+)/gi;
var newstr = str.replace(reg, '$2 $1');
console.log(newstr); // 输出结果为:Capture Regular
- 第1个参数为正则表达式,用于与str字符串进行匹配,将符合规则的内容利用第2个参数设置的内容进行替换。
- $2表示reg正则表达式中第2个子表达式被捕获的内容“Capture”。
- $1表示第1个子表达式被捕获的内容“Regular”。
- 返回值是替换后的新字符串,因此,并不会修改原字符串的内容。
// 非捕获
var reg = /(?:J)(?:S)/;
var res = 'JS'.replace(reg,'$2 $1');
console.log(res); // 输出结果:$2 $1
// 捕获
var reg = /(J)(S)/;
var res = 'JS'.replace(reg,'$2 $1');
console.log(res); // 输出结果:S J
反向引用
在编写正则表达式时,若要在正则表达式中,获取存放在缓存区内的子表达式的捕获内容,则可以使用“\n”(n是大于0的正整数)的方式引用,这个过程就是“反向引用”。
- “\1”表示第1个子表达式的捕获内容。
- “\2”表示第2个子表达式的捕获内容,以此类推。
var str = '13335 12345 56668';
var reg = /(\d)\1\1/gi;
var match = str.match(reg);
console.log(match); // 输出结果为:(2) ["333", "666"]
- “\d”用于匹配0~9之间的任意一个数字。
- 为其添加圆括号“()”后,即可通过反向引用获取捕获的内容。
- 因此,最后的匹配结果为333和666。
零宽断言
指的是一种零宽度的子表达式匹配,用于查找子表达式匹配的内容之前或之后是否含有特定的字符集。
分为正向预查和反向预查,但是在JavaScript中仅支持正向预查,即匹配含有或不含有捕获内容之前的数据,匹配的结果中不含捕获的内容。
字符 | 说明 | 示例 |
---|---|---|
x(?=y) | 仅当x后面紧跟着y时,才匹配 x | Countr(?=y|ies)用于匹配Country或Countries中的Countr |
x(?!y) | 仅当x后不紧跟着y时才匹配 x | Countr(?!y|ies)用于匹配Countr后不是y或ies的任意字符串中的Countr |
3.3 正则运算符优先级
正则表达式中的运算符有很多。在实际应用时,各种运算符会遵循优先级顺序进行匹配。正则表达式中常用运算符优先级,由高到低的顺序如下表。
运算符 | 说明 |
---|---|
\ | 转义符 |
()、(?:)、(?=)、[] | 括号和中括号 |
*、+、?、{n}、{n,}、{n,m} | 限定符 |
^、$、\任何元字符、任何字符 | 定位点和序列 |
| | “或”操作 |
3.4 练习作业
- 内容查找与替换
- 编写HTML,定义两个文本域,一个用于用户输入,另一个用于显示按照要求替换后的过滤内容。
- 用户输入只要含有bad和任意中文字符,就利用*替换。
- 查找并替换的内容规则:/(bad)|[\u4e00-\u9fa5]/gi。
4 与正则相关的方法
4.1 RegExp 类中的方法
test()方法:检测正则表达式与指定的字符串是否匹配。
var reg = /([A-Z])([A-Z])\1\2/g;
console.log(reg.test('1234')); // 输出结果为:false
console.log(reg.test('abab')); // 输出结果为:false
console.log(reg.test('CDCD')); // 输出结果为:true
console.log(reg.test('EfEf')); // 输出结果为:false
匹配成功时,test()方法的返回值为true,否则返回false。
检测正则对象的模式修饰符
RegExp类中还有一些属性,用于检测当前正则对象使用的模式修饰符,以及指定下一次匹配的起始索引等。
属性 | 说明 |
---|---|
global | 检测正则表达式中是否使用g模式修饰符,使用返回true,否则返回 false |
ignoreCase | 检测正则表达式是否使用了i 模式修饰符,使用返回true,否则返回false |
multiline | 检测正则表达式是否使用了m模式修饰符,使用返回true,否则返回false |
lastIndex | 全局匹配时用来指定下一次匹配的起始索引 |
source | 返回正则表达式对象的模式文本的字符串,该字符串不包含正则字面量两边的斜杠以及任何的模式修饰字符 |
var reg = /[\s+]/g;
console.log(reg.exec('h i'));
console.log(reg.lastIndex); // 输出结果为:2
console.log(reg.source); // 输出结果为:[\s+]
console.log(reg.global); // 输出结果为:true
console.log(reg.ignoreCase); // 输出结果为:false
console.log(reg.multiline); // 输出结果为:false
4.2 String 类中的方法
search()方法:可以返回指定模式的子串在字符串首次出现的位置,相对于indexOf()方法来说功能更强大。
var str = '123*abc.456';
console.log(str.search('.*')); // 输出结果:0
console.log(str.search(/[\.\*]/)); // 输出结果:3
- search()方法的参数是一个正则对象,如果传入一个非正则表达式对象,则会使用“new RegExp(传入的参数)”隐式地将其转换为正则表达式对象。
- search()方法匹配失败后的返回值为-1。
split()方法:用于根据指定的分隔符将一个字符串分割成字符串数组,其分割后的字符串数组中不包括分隔符。
当分隔符不只一个时,需要定义正则对象才能够完成字符串的分割操作。
// 按照规则分割
var str = 'test@123.com';
var reg = /[@\.]/;
var split_res = str.split(reg);
console.log(split_res); // 输出结果:(3) ["test", "123", "com"]
- 按照字符串中的“@”和“.”两种分隔符进行分割。
- split()方法的参数为正则表达式模式设置的分隔符,返回值是以数组形式保存的分割后的结果。
当字符串为空时,split()方法返回的是一个包含一个空字符串的数组“[“”]”,如果字符串和分隔符都是空字符串,则返回一个空数组“[]”。
// 指定分割次数
var str = 'We are a family';
var reg = /\s/;
var split_res = str.split(reg, 2);
console.log(split_res); // 输出结果:(2) ["We", "are"]
- 在使用正则匹配方式分割字符串时,还可以指定字符串分割的次数。
- 当指定字符串分割次数后,若指定的次数小于实际字符串中符合规则分割的次数,则最后的返回结果中会忽略其他的分割结果。
5 练习作业
- 表单验证
- 用户名:长度4~12,英文大小写字母。正则:/^[a-zA-Z]{4,12}$/。
- 密码:长度6~20,大小写字母、数字或下划线。正则:/^\w{6,20}$/。
- 确认密码:要求与密码框一样,且两次输入相同。正则:RegExp(‘^’ + 密码框的值 + '$')
- 手机号码:13、14、15、17、18开头的11位手机号。正则:/^1[34578]\d{9}$/。
- 邮箱:用户名 @ 域名(域名后缀至少2个字符)。正则:/^(\w+(_|-|.)*)+@(\w+(-)?)+(.\w{2,})+$/。
- 编写用户注册表单,包含用户名、密码、确认密码、手机号以及电子邮箱。
- 表单项添加失去焦点处理,事件处理函数为 inputBlur()。
- 编辑 inputBlur() 函数,获取表单 name、value 及提示信息后,去除空白后,若内容为空调用 error() 给出提示,否则进行验证。
- 编写 error() 函数完成错误提示
- 编写 getRegMsg() 函数获取验证的正则及提示信息。
- 根据正则验证 value 值,成功调用 success() 给出通过的提示。
- 编写 success() 函数完成错误及成功的提示。