本篇我们来学习一个强大的工具–正则表达式,正则表达式发展到今天,可以说已经成为诸多编程语言的一部分了,通过正则表达式,我们可以更精确更舒适的掌控我们的数据。
正则表达式(Regular Expression)是强大、便捷、高效的文本处理工具。其如同一门编程语言的通用模式表示法,赋予使用者描述、分析和操作文本的能力。
正则表达式的结构
- 一个完整的正则表达式由小的构建模块单元(building block unit)组成;
- 完整的正则表达式由两种字符构成:特殊字符(元字符)和普通文本字符(文字)。
元字符
本节介绍JavaScript中正则表达式支持的元字符:
行的起始和结束–^&$
元字符中脱字符代表一行的开始,美元符号$代表一行的结束:如test,匹配的是以t作为一行第一个字符,紧接一个e,紧接一个s,紧接一个t的文本。
匹配任意字符–点号.
元字符点号.用来匹配任意字符
表示范围–连字符-
连字符-表示一个范围,如[0-9]表示匹配0到9的任意一个数字。
注:只有在字符组中且不是字符组的第一个字符时,连字符-才是元字符,才表示一个范围,此外,其就是一个普通文本字符。
元字符 | 描述 |
---|---|
^ | 匹配一行的起始 |
$ | 匹配一行的结束 |
. | 匹配单个字符,除了换行符和行结束符 |
– | 表示某一范围,尽在字符组中为元字符 |
竖线 | 匹配总表达式中任一子表达式 |
\w | 匹配单词字符 |
\W | 匹配非单词字符 |
\d | 匹配数字 |
\D | 匹配非数字字符 |
\s | 匹配空白字符 |
\S | 匹配非空白字符 |
\b | 匹配单词边界 |
\B | 匹配非单词边界 |
\n | 匹配换行符 |
\xxx | 匹配以八进制数xxx规定的字符 |
\xdd | 匹配以十六进制数dd规定的字符 |
\uxxxx | 匹配以十六进制数xxxx规定的Unicode字符 |
() | 限定多选结构的范围,标注量词作用的元素, 为反向引用“捕获”文本 |
$1,$2或\1,\2 | 匹配之前的第一、第二组括号内的表达式匹配文本 |
字符组
通常我们需要匹配某些相似的文本如gray和grey,这时可以使用正则表达式的结构体之字符组,用中括号[]表示;如gr[ae]y表示匹配gray或者grey。可以说,在字符组中的待匹配字符是或的关系,整个字符组表示匹配若干字符之一。若在字符组内第一个字符为,如[ae]为排除型字符组,表示匹配除a和e之外的任一字符
表达式 | 描述 |
---|---|
[abc] | 匹配括号内的字符之一 |
[^abc] | 匹配不在括号内的某一字符 |
[0-9] | 匹配任一从0至9的数字 |
[a-z] | 匹配任一从小写a到小写z的字符 |
[A-Z] | 匹配任一从大写A到大写Z的字符 |
多选结构
匹配任意子表达式
在正则表达式中,我们可以把不同的子表达式组合成一个总表达式,这个总表达式又能够匹配任一子表达式,只需用元字符|;如(gray|grey)或gr(a|e)y等价于之前的gr[ae]y,这些子表达式就是“多选分支”。
量词
可选项元素
元字符问号?代表可选项,把它加在一个字符的后面,就表示此处容许出现这个字符,可以出现0次或1次。如:colou?r可匹配color或colour。
重复出现
加号+和星号*的作用与问号都是正则表达式里的量词,表示字符可匹配次数。
- 加号字符+表示其之前紧邻的元素出现一次或多次;
- 星号*表示其之前紧邻的元素出现0或任意多次。
注:一个字符组就是一个“元素”,不需要括号就可直接使用加号、星号、问号等量词。
区间
在JavaScript正则表达式中,可以使用元字符序列来自定义文本出现次数的区间:如:{1,6}表示允许其之前元素出现次数为1到6。
之前的问号量词,等价于区间量词{0,1}
量词 | 描述 |
---|---|
? | 匹配0或1次 |
+ | 1或多次 |
* | 匹配0或多次 |
{x,y} | 匹配从x到y次(包括x和y次) |
括号和反向引用
括号的作用:
- 限制多选项的范围;
- 将若干字符组合为一个单元,受问号等量词的作用;
- 反向引用:允许匹配与表达式之前部分匹配的相同文本。
捕获组
使用小括号指定一个子表达式,这个小括号及子表达式就是一个捕获组,而子表达式匹配到的文本就是这个捕获组的内容。默认情况,正则能记下捕获组:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,依此类推。
var str = 'abbcde';
var reg = /([ab])\\1/img
reg.exec(str);
表达式 | 描述 |
---|---|
(pattern) | 匹配pattern并捕获结果 |
(?<name>pattern)或(?’name’pattern) | 匹配pattern并捕获结果,设置name为组名。 |
\1 | 对捕获组的反向引用 |
\k<name>或\k’name’ | 对命名捕获组的反向引用。其中 name 是捕获组名。 |
非捕获组
既然有捕获组,那肯定还有非捕获组。顾名思义,只执行匹配过程、不保存匹配结果的分组就是非捕获组。在捕获组表达式的左括号后加“?”和“:”等符号即可构成非捕获组。
/(?:pattern)/ // JS 中非捕获组的基本写法
环视
想一想,假如我们需要在一个大整数中间从右到左每隔三个数字插入一个逗号,如12034056078变成12,034,056,078,如何实现呢?我们可以从右至左,每次数三位数字,如果左边还有数字就插入一个逗号,即逗号应该插入在右边数字为3的倍数,左边有数字的位置。这种思想就是“环视”(lookaround)。
环视结构不匹配任何字符,只匹配文本中的特定位置,就像行起始符和结束符。
顺序环视
顺序环视从左至右查看文本,尝试匹配子表达式,若匹配成功,则返回匹配成功信息。
- 肯定顺序环视用特殊序列(?=…)表示,如(?=\d),表示如果当前位置右边的字符是数字则匹配成功;
- 否定顺序环视用特殊序列(?!…)表示,如(?!\d),表示如果当前位置右边的字符不是数字则匹配成功。
逆序环视
逆序环视从右至左查看文本,尝试匹配子表达式,若匹配成功,则返回匹配成功信息。
- 肯定逆序环视用特殊序列(?<=…)表示,如(?<=\d),表示如果当前位置左边的字符是数字则匹配成功;
- 否定逆序环视用特殊序列(?<!…)表示,如(?<!\d),表示如果当前位置左边的字符不是数字则匹配成功。
JavaScript只支持顺序环视。
类型 | 表达式 | 描述 |
---|---|---|
肯定顺序环视 | (?=…) | 子表达式能够匹配右侧文本 |
否定顺序环视 | (?!…) | 子表达式不能匹配右侧文本 |
肯定逆序环视 | (?<=…) | 子表达式能够匹配左侧文本 |
否定逆序环视 | (?<!…) | 子表达式不能匹配左侧文本 |
所以之前提出给数字插入逗号的解决方法可以如下:
var str = 'w12034056078';
var reg = /(\\d)(?=(\\d{3})+(?!\\d))/img;
var reg1 = /(?=(\\d{3})+(?!\\d))/g;
str.replace(reg, "$1,");//输出w12,034,056,078
str.replace(reg1, ",");//输出w12,034,056,078
修饰符
定义正则表达式时,我们可以通过设置不同修饰符属性以定义正则表达式不同匹配模式。JavaScript中有三种修饰符:
类型 | 符号 | 描述 |
---|---|---|
大小写 | i | 执行忽略大小写的匹配 |
多行 | m | 执行多行匹配 |
全局 | g | 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。 |
JavaScript正则实践
RegExp对象
在JavaScript中,RegExp对象表示正则表达式,它是对字符串执行模式匹配的强大工具。
正则表达式实践
- 直接量定义
/pattern/attributes
如下表示匹配数字且字符次数为3的倍数:
var reg = /(\\d{3})+/img;
- 创建RegExp对象定义
new RegExp(pattern, attributes);
var reg = new RegExp("(\\d{3})+", 'igm');
注:参数 pattern 是一个字符串,指定了正则表达式的模式或其他正则表达式。
参数 attributes 是一个可选的字符串,包含属性 “g”、”i” 和 “m”,分别用于指定全局匹配、区分大小写的匹配和多行匹配。
RegExp对象属性
属性 | 描述 |
---|---|
global | RegExp 对象是否具有标志 g。 |
ignoreCase | RegExp 对象是否具有标志 i。 |
multiline | RegExp 对象是否具有标志 m。 |
lastIndex | 一个整数,标示开始下一次匹配的字符位置。 |
source | 正则表达式的源文本。 |
var str = "The rain in Spain stays mainly in the plain";
var patt1 = new RegExp("ain", "g");
patt1.test(str);
console.log(patt1.lastIndex); //输出8
var patt2 = new RegExp("e","g");
var result;
do
{
result = patt2.exec("The best things in life are free");
document.write(result + ":" + patt2.lastIndex + "<br />");
}
while (result != null)
//输出e:6 e:23 e:27 e:31 e:32 null:0
RegExp对象方法
方法 | 描述 |
---|---|
exec | 检索字符串中指定的值。返回找到的值,并确定其位置。 |
test | 检索字符串中指定的值。返回 true 或 false。 |
var str = "We are family";
var reg = new RegExp("ami","g");
var result = reg.exec(str);
var result2 = reg.test(str);
console.log(result); //输出["ami", index: 8, input: "We are family"]
console.log(result2); //输出true
正则表达式相关的String对象方法
方法 | 描述 |
---|---|
match | 找到一个或多个正则表达式的匹配。stringObject.match(searchvalue)或stringObject.match(regexp) |
search | 检索与正则表达式相匹配的值,返回字符串中第一个与regexp相匹配的子串的起始位置。如果没有找到任何匹配的子串,则返回 -1。stringObject.search(regexp) |
replace | 替换与正则表达式匹配的子串。stringObject.replace(regexp/substr,replacement) |
split | 把字符串分割为字符串数组。stringObject.split(separator,num),num参数可选,指定返回的数组的最大长度。 |
注:replace方法参数中replacement可以是字符串,也可以是函数。如果它是字符串,那么每个匹配都将由字符串替换。但是replacement中的 $ 字符具有特定的含义,它说明从模式匹配得到的字符串将用于替换,如$1、$2、…、$99,替换文本分别为与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。
//search
var str="We are family!"
console.log(str.search(/Family/i)); //输出7
//match
var str="1 plus 2 equal 3"
str.match(/\\d+/g);//输出["1", "2", "3"]
//replace
str.replace(/\\d+/g, function(v) {
return (parseInt(v) + 1);
}); //输出"2 plus 3 equal 4"
var name = "Doe, John";
name.replace(/(\\w+)\\s*, \\s*(\\w+)/, "$2 $1"); //输出"John Doe"
//split
"h1e2l3l4o".split(/\\d/g, 3); //输出 ["h", "e", "l"]