4

简介

正则表达式,又称正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。

说人话就是:对字符串执行模式匹配的强大工具。

Javascript中的正则表达式相较于其他语言来说实现的并不完整,但仍然是十分强大的,能够帮助我们完成绝大多数模式匹配任务。

定义

在javascript中,正则表达式的定义和数组以及对象的定义一样有俩种方式。 一种是直接量,另一种是构造函数。

直接量

Javascript可以使用类似Perl的语法定义一个正则表达式

var r = /pattern/flags;

其中pattern是任何简单或复杂的正则表达式,flags是用以表达正则表达式行为的一个或者多个标识。

flags

  • g 表示全局(global)模式,即模式将被应用于所有字符串

  • i 表示不区分大小写

  • m 多行模式

举个栗子:

var reg1 = /ip/g; // 匹配字符串中所有'ip'的实例

var reg2 = /com/gi; // 匹配以'com'结尾的三个字符的组合,不区分大小写

使用构造函数

Javascript中正则表达式的构造函数为RegExp,即Regular Expression的缩写,它接收两个参数,第一个为要匹配的字符串模式,另一个是可选的标识位。

var reg = new RegExp('nice', 'g'); // 匹配字符串中所有的'nice'

注意:第一个参数是一个字符串nice,而不是正则表达式的直接量/nice/

双重转义

所有元字符都需要进行双重转义

所有元字符都需要进行双重转义

所有元字符都需要进行双重转义

使用构造函数定义正则表达式,由于构造函数的第一个参数必须是一个字符串,在匹配元字符时,需要双重转义。

//  匹配{user}
var reg1 = new RegExp('\{user\}', 'g'); // wrong

// 由于\在字符串中需要转义为\\,所以,如果要匹配{user},正则表达式需要写为
var reg1 = new RegExp('\\{user\\}', 'g');

基本概念总结

元字符

在正则表达式的模式中,有一些字符是有特殊含义的,被称为元字符,如果要匹配元字符,则必须对其进行转义,如果用构造函数,则要进行双重转义。

这些元字符分别是(一共14个):

{ ( [  \ ^ $ |  ? * + . ] ) }

直接量字符

| 字符 | 匹配 |
| ------------- |:-------------:|
|字母数字字符|自身|
|\0 |查找 NUL 字符。|
|\n |查找换行符。|
|\f |查找换页符。|
|\r |查找回车符。|
|\t |查找制表符。|
|\v |查找垂直制表符。|
|\xxx |查找以八进制数 xxx 规定的字符。|
|\xdd |查找以十六进制数 dd 规定的字符。|
|\uxxxx |查找以十六进制数 xxxx 规定的 Unicode 字符。|

不管一个直接量字符字符代表何种含义,单个字符始终只匹配单个字符

字符类

| 字符 |等价于| 匹配 |
| ------------- |:-------------:| :-------------:|
| . | 1|查找单个字符,除了换行和行结束符。 |
| \w| [a-zA-Z_0-9]|任意ASCII单字字符,等价于[a-zA-Z0-9_]|
| \W| 2 |查找非单词字符。 |
|\d |[0-9]|查找数字。|
|\D |3|查找非数字字符。|
|\s |[ \t\n\x0B\f\r]|查找空白字符。|
|\S |4|查找非空白字符。|
|[]||来表示单个字符有或的关系,如/[bc]/,匹配b或者c|
|^||来表示不匹配后面跟着的字符,如/5,/不匹配b或c|
|-||来表示一个范围,如/[a-z]/,匹配a-z的字母,可以和负向类结合/^0-9/|
|组合类:||以上几种类组合而成的字符类,如/a-z0-9\n/,匹配a-z的字母或者0-9的数字或者换行符|

注意:在javascript类不能嵌套,就是不支持联合类和交叉类,/a-m[p-z]/和/a-m6/在JavaScript中是非法的

锚字符

| 字符 | 含义|
| ------------- |:-------------:|
| ^ | 匹配字符串的开头,在多行检索中,匹配一行的开头|
| $| 匹配字符串的结尾,在多行检索中,匹配一行的结尾|
|\b |匹配单词边界。|
|\B |匹配非单词边界。|

量词

简单量词

| 常用量词 | {m,n}等价形式|描述 |
| ------------- |:-------------:| :-------------:|
|?|{0,1}|最多出现1次,例如,do(es)? 匹配 do 或 does 中的 do|
|* |{0,}|可能出现,也可能不出现,出现次数无上限,例如,zo* 匹配 z 和 zoo。|
|+ |{1,}|最少出现1次,例如,zo+ 匹配 zo 和 zoo,但不匹配 z|

在正则表达式概念中,称 +、*、?为常用量词,称{n,m}形式的为通用量词。

使用情景举例

**?的使用
**
情景:美式应用和英式语言的单词写法不同,比如traveler和traveller.

var str1 = 'traveler';
var str2 = 'traveller';
var pattern = /travell?er/;
console.log(pattern.test(str1));
console.log(str2.search(str2));

结果打印:
true
0

匹配各类标签(前端应用)

  • 匹配所以tag: /<7+>/

  • 匹配open tag: /<87*>/

  • 匹配close tag: /</7+>/

  • 匹配self-closing tag: /9+/>/

复杂量词

  • 贪婪量词:先匹配整个字符串,如果不匹配则去掉最后一个字符再匹配,直到没有任何字符。它是从后向前匹配。所有简单量词都是贪婪的

  • 惰性量词:和贪婪量词相反,即先匹配第一个字符,不匹配则匹配第一第二个字符,直到最后整个字符串。所有简单量词后面加?就是惰性的了

  • 支配量词:只匹配整个字符串一次,不匹配就结束,也称最懒量词,所有简单量词后面加+就是支配的了,但是javascript不支持,所以它连出场的机会也没有了。

举个栗子(complex-quantifiers.js):

var re1 = /.*bbb/g;        //  定义贪婪量词
var re2 = /.*?bbb/g;    //  定义简单惰性
//  var re3 = /.*+bbb/g;//支配性,javascript不支持,nodejs报错
var  str='abbbaabbbaaabbb1234';

console.log(re1.test(str));  //true
console.log(re1.exec(str));  //null
console.log(str.match(re1));  //abbbaabbbaaabbbb

console.log(re2.test(str));  //true
console.log(re2.exec(str));  //aabbb
console.log(str.match(re2)); //abbb,aabbb,aaabbb

贪婪模式详解

分组

到目前为止,我们只能一个字符到匹配,虽然量词的出现,能帮助我们处理一排密紧密相连的同类型字符。但这是不够的,下面该轮到小括号出场了,中括号表示范围内选择,大括号表示重复次数。小括号允许我们重复多个字符。

举个栗子(group.js):

//分组+量词
console.log(/(dog){2}/.test("dogdog"))//true
//分组+范围
console.log("baddad".match(/([bd]ad?)*/))//baddad,dad
//分组+分组
console.log("mon and dad".match(/(mon( and dad)?)/))//mon and dad,mon and dad, and dad

优先级

|优先级| 符号|说明|
| ------------- |:-------------:| :-------------:|
|最高 |\|转义符|
|高 |( )、(?: )、(?= )、[ ]|单元和字符类|
|中 |*、+、?、{n}、{n,}、{m,n}|量词|
|低 |^、$、\任何元字符、任何字符|描点|
|最低|\||替换,选择|

参考加减乘除,先算括号内的记忆方式

RegExp对象

RegExp属性

global 属性 | ignoreCase 属性 | multiline 属性 | source 属性

  • global: 返回布尔值,该值指示使用正则表达式的 global 标志 (g) 的状态。 默认值为 false。 只读。

  • ignoreCase: 返回布尔值,该值指示在正则表达式中使用的 ignoreCase 标志 (i) 的状态。 默认值为 false。 只读。

  • multiline: 返回布尔值,该值指示在正则表达式中使用的 multiline 标志 (m) 的状态。 默认值为 false。 只读。

  • source: 返回正则表达式模式的文本的一个副本。 只读。

  • lastIndex: 如果使用了全局模式,这个变量保存的是字符串中尝试下次匹配的偏移值。在exec和test中都会用到这个变量。 可写

RegExp方法

RegExp 对象有 3 个方法:test()、exec() 以及 compile()

RegExp.test(string)

test 方法检查字符串中是否存在某种模式,如果存在,则返回 true,否则返回 false。test 方法不修改全局 RegExp 对象的属性。

语法:

rgExp.test(str) 

//  参数
rgExp 必需。 包含正则表达式模式和适用标志的 Regular Expression 对象的实例。
str 必需。 将对其执行搜索的字符串。

栗子:

var str = 'huangge is an handsome man';

var reg1 = new RegExp( 'handsome', 'g' );
var result = reg1.test(str);
console.log('huangge is an handsome man: ' + result);
RegExp.exec(string)

exec 方法使用正则表达式模式对字符串执行需找匹配,如果成功,则返回表示匹配信息的数组,否则返回null。默认情况下,它返回第一次匹配的结果

语法:

rgExp.exec(str) 

//  参数
rgExp 必需。 包含正则表达式模式和适用标志的 Regular Expression 对象的实例。
str 必需。 对其执行搜索的 String 对象或字符串文本。

返回值

如果成功匹配,exec 方法返回一个数组,并且更新正则表达式对象的属性。返回的数组包括匹配的字符串作为第一个元素,紧接着一个元素对应一个成功匹配被捕获的字符串的捕获括号(capturing parenthesis)。

如果匹配失败,exec 方法将返回 null。

栗子(regexp-exec.js):

var reg= /\d{4}-\d{2}-\d{2}/g;
var str = 'aaa2015-08-11aaaaaabbss2015-08-22bbb';
var result = reg.exec(str);
console.log(result);


结果如下:

[ '2015-08-11',
  index: 3,
  input: 'aaa2015-08-11aaaaaabbss2015-08-22bbb' ]
RegExp.compile(string)

将正则表达式编译为内部格式,从而更快地执行。

语法:

rgExp.compile(pattern, [flags]) 

//  参数
rgExp 必需。 Regular Expression 对象的一个实例。 可以是变量名或文本。
pattern 必需。 一个字符串表达式,包含要编译的正则表达式模式
flags 可选。 可以组合使用的可用标志有:
    g(全局搜索出现的所有模式)
    i(忽略大小写)
    m(多行搜索)

compile 方法将 pattern 转换为内部的格式,从而执行得更快。 例如,这允许在循环中更有效地使用正则表达式。 当重复使用相同的表达式时,编译过的正则表达式使执行加速。 然而,如果正则表达式发生更改,则这种编译毫无益处。

String的正则方法

String.match(RegExp)

这个方法类似RegExp.exec(string),只是调换了RegExp和string的位置。 另一个区别就是,无论是否指定全局,RegExp.exec(string)只是返回单词匹配结果。 而string.match()会返回一个字符串数组,其中包含了各次匹配成功的文本

举个栗子(string-match.js):

var reg= /\d{4}-\d{2}-\d{2}/g;
var str = 'aaa2015-08-11aaaaaabbss2015-08-22bbb';
var result = str.match(reg);
console.log(result);

打印的结果为:

[ '2015-08-11', '2015-08-22' ]
String.search(RegEexp)

这个用来寻找某个正则表达式在字符串第一次匹配成功的位置,如果不成功,则返回-1

举个栗子(string-search.js):

var str1 = 'sssssaa111';
var str2 = 'sssssaaaaa';

var pattern = /\d/;
console.log ('st1  ' + str1.search(pattern));
console.log ('st2  ' + str2.search(pattern));

打印的结果为:

st1  7
st2  -1
String.replace(RegExp,replacement)

这个方法用来正则表达式替换。 将匹配到的文本替换为replacement。默认情况替换一次。如果设定了全局模式,则所有能匹配的文本都会替换。
举个栗子(string-replace.js):

var reg = /\d{4}-\d{2}-\d{2}/;
var str = '2015-08-11 2015-08-22';

console.log(str.replace(reg, 'Date:'));

打印的结果为:

ate: 2015-08-22
String.split(RegExp [,limit])

这个方法使用一个正则表达式切分字符串,正则表达式是否使用全局对结果没有影响
举个栗子(string-split.js):

var str = 'one two  three    four';
var pattern = /\s+/;
console.log(str.split(pattern))

打印的结果为:

[ 'one', 'two', 'three', 'four' ]

常用的正则表达式

  • 匹配中文字符的正则表达式: [\u4e00-\u9fa5]

  • 匹配双字节字符(包括汉字在内):10
    评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

  • 匹配空白行的正则表达式:\n\s*\r

  • 匹配HTML标记的正则表达式:<(\S?)7>.?</\1>|<.? />

  • 匹配首尾空白字符的正则表达式:^\s|\s$

  • 匹配Email地址的正则表达式:\w+([-+.]\w+)@\w+([-.]\w+)\.\w+([-.]\w+)*

  • 匹配网址URL的正则表达式:[a-zA-z]+://11*

  • 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^a-zA-Z{4,15}$

  • 匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7}

  • 匹配腾讯QQ号:1-9{4,}

  • 匹配中国邮政编码:[1-9]\d{5}(?!\d)

  • 匹配身份证:\d{15}|\d{18}

  • 匹配ip地址:\d+\.\d+\.\d+\.\d+

匹配特定数字

^[1-9]\d*$    //匹配正整数
^-[1-9]\d*$   //匹配负整数
^-?[1-9]\d*$   //匹配整数
^[1-9]\d*|0$  //匹配非负整数(正整数 + 0)
^-[1-9]\d*|0$   //匹配非正整数(负整数 + 0)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$   //匹配正浮点数
^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$  //匹配负浮点数
^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$  //匹配浮点数
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$   //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$  //匹配非正浮点数(负浮点数 + 0)

匹配特定字符串

^[A-Za-z]+$  //匹配由26个英文字母组成的字符串
^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串
^[a-z]+$  //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串
^\w+$  //匹配由数字、26个英文字母或者下划线组成的字符串

  1. \n\r
  2. a-zA-Z_0-9
  3. 0-9
  4. \t\n\x0B\f\r
  5. bc
  6. b-e
  7. >
  8. />
  9. >/
  10. \x00-\xff
  11. \s

苏理煌
170 声望2 粉丝