创建正则表达式

JS的正则表达式类型叫RegExp,有2种创建方式:

var reg1 = /a/g; // 双斜杠法,这种方法书写更方便

var reg2 = new RegExp('a', 'g'); // 传统写法,这种方法更正统

console.log(/a/g instanceof RegExp); // 输出true

这2种方式没有任何区别,但是,当正则表达式的内容是动态的时候,用RegExp会更方便一点,用双斜杠法的话你可能需要使用eval来创建。

上面的g表示修饰符,完整修饰符包括:

g: 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)

i: 忽略大小写

m: 允许多行匹配

这3个修饰符没有先后顺序,可以随便写,我一般习惯写成gim:

var reg1 = /a/gim;

var reg2 = new RegExp('a', 'gim');

JS中所有支持正则表达式的方法

字符串的4个和正则相关方法

str.match(reg)

返回所有匹配的结果并放入数组中,如果没找到,返回null,示例:

'aaa_aa_a_bbb_bb'.match(/a+_/g); // ["aaa_", "aa_", "a_"]

str.replace(reg, newStr|fn)

replace的第1个参数可以是一个正则表达式,也可以是一个普通的字符串,replace的第2个参数可以是一个字符串,也可以是一个function,返回需要替换成的目标字符串:

'http://blog.haoji.me/index.html?a=1'.replace(/(https?):\/\/([^\/]+)([^\?]+)/g, function(m, $1, $2, $3, idx, str) {

console.log(m, $1, $2, $3, idx, str);

return m;

});

这个function的参数依次是:

m:本次匹配到的结果;

$1:第一个()匹配到的结果;

$2:第二个()匹配到的结果(以此类推,有多少个括号就有多少个$,最多9个);

idx:本次匹配的结果在原始字符串中的索引;

str:原始字符串;

str.search(reg)

返回某个字符串第一个匹配处的索引,有点类似于indexOf,但indexOf不支持正则,示例:

'aaa_aa_a_bbb_bb'.search(/b+_/g); // 9

str.split(char|reg)

对字符串进行分割,可以传入一个正则:

'aaa_aa-a_bbb+bb'.split(/[-_\+]/g); // ["aaa", "aa", "a", "bbb", "bb"]

正则下的2个方法

reg.exec(str)

每执行一次,返回一次当前匹配的结果(放入数组,输入内容为[m, $1, $2, ...]),每次会记住上次的位置(通过reg.lastIndex属性),全部匹配完之后又从头开始,示例:

var reg = /a(\w+?)_/g;

var str = 'abb_acc_ad_bbb_bb';

console.log(reg.exec(str)); // ["abb_", "bb"]

console.log(reg.exec(str)); // ["acc_", "cc"]

console.log(reg.exec(str)); // ["ad_", "d"]

console.log(reg.exec(str)); // null

console.log(reg.exec(str)); // ["abb_", "bb"]

关于lastIndex,参见后文。

reg.test(str)

这个可能用的最多了,用以测试某个字符串是否满足某个正则表达式,示例:

/^1[345789]\d{9}$/g.test('18911112222'); // 测试手机号

匹配包括换行符在内的任意字符

由于.*只是匹配除\n之外的任何字符,所以要匹配包括换行符在内的任意字符的话,最常见做法是[\s\S]*(以此类推还可以[\d\D]*、[\w\W]*),或者(.|\n)*,但是后面一种写法有一个缺点就是会将匹配的结果放到$1-$9中去,影响其它顺序,不推荐。

var str = '



\ntest

';


console.log(/



console.log(/



有人会提出,为什么不用[.\n]*呢,因为在中括号[]中,.只是表示普通的英文字母点,加不加斜杠都一样,证明如下:

console.log('.aaa.bbb.ccc'.match(/[.]\w/g)); // 输出 [".a", ".b", ".c"]

console.log('.aaa.bbb.ccc'.match(/[\.]\w/g)); // 输出 [".a", ".b", ".c"]

正则表达式对象是会“变”的

当然,这里所说的会“变”是打了引号的,为了更好描述问题,我们先来看个例子:

var reg = /a\w/g;

console.log(reg.exec('abbaccadd')[0]); // 输出ab

console.log(reg.exec('abbaccadd')[0]); // 输出ac

console.log(reg.exec('abbaccadd')[0]); // 输出ad

console.log(/a\w/g.exec('abbaccadd')[0]); // 输出ab

console.log(/a\w/g.exec('abbaccadd')[0]); // 输出ab

解释:

每一个RegExp对象都会有一个lastIndex字段(详情可以查看w3cschool上面有关lastIndex的介绍),当你每执行完一遍exec,它都会将最新匹配到的位置保存到对象的lastIndex中,所以每次结果都不一样,直接使用/a\w/g的话相当于是每次都new了一个RegExp对象。

var reg = /a\w/g;

console.log(reg.lastIndex); // 输出 0

console.log(reg.exec('abbaccadd')[0]); // 输出ab

console.log(reg.lastIndex); // 输出 2

特别说明:

不具有标志 g 和不表示全局模式的 RegExp 对象不能使用 lastIndex 属性。

如果在成功地匹配了某个字符串之后就开始检索另一个新的字符串,需要手动地把这个属性设置为 0。

所以为了避免出现上述情况,我们有3种方法避免:

// 第一种方法,每次都使用新的RegExp对象,推荐

console.log(/a\w/g.exec('abbaccadd')[0]); // 输出ab

console.log(/a\w/g.exec('abbaccadd')[0]); // 输出ab

console.log(/a\w/g.exec('abbaccadd')[0]); // 输出ab

// 第二种方法,不使用g标志,推荐

var reg = /a\w/;

console.log(reg.exec('abbaccadd')[0]); // 输出ab

console.log(reg.exec('abbaccadd')[0]); // 输出ab

console.log(reg.exec('abbaccadd')[0]); // 输出ab

// 第三种方法,每次调用完之后手动重置lastIndex为0,不推荐

var reg = /a\w/g;

console.log(reg.exec('abbaccadd')[0]); // 输出ab

reg.lastIndex = 0;

console.log(reg.exec('abbaccadd')[0]); // 输出ab

reg.lastIndex = 0;

console.log(reg.exec('abbaccadd')[0]); // 输出ab

使用test时特别注意undefined

看下面的例子:

var str = '';

if(!/^\w+$/g.test(str)) console.error('str只能是字母、数字或下划线!');

else console.log('您的输入合法!')

表面看没什么问题,但是如果把str换成var str = undefined;时会发现也会测试通过,所以使用reg.text(str)切记要先判断str不能是undefined,否则会出现问题。

语法篇

JS不支持反向预查

预查,又叫零宽断言,一共有4种,最大的作用就是匹配结果不包含括号里面的内容。

(?=pattern):正向肯定预查,如Windows(?=95|98|NT)可以匹配Windows95中的Windows;

(?!pattern):正向否定预查,如Windows(?!95|98|NT)能匹配Windows97中的Windows;

(?<=pattern):反向肯定预查,如(?<=95|98|NT)Windows可以匹配95Windows中的Windows;

(?

放在前面的叫正向预查(有的地方叫先行断言),放在后面的叫反向预查(有的地方叫后行断言)

需要特别注意的是,JS是不支持后面2种的!也就是说JS不支持(?<=pattern)和(?

Uncaught SyntaxError: Invalid regular expression:/(?<=95|98|NT)Windows/: Invalid group(…)

ExpressionFactory 解析表达式_字符串

关于为什么不支持,有的人说是设计者忘了,有的人说是出于性能考虑,具体是为什么也没人知道,反正知道至今JS仍然不支持这个就对了,其他语言(比如Java、C#)都是支持的。

特别说明:ES6已经开始支持反向预查了(Chrome 62已经开始支持)。

常见示例

HTML标签匹配

匹配title和description:

var html = 'xxx';

var title = /

[\s\S]*?([\s\S]*?)[\s\S]*?/gim.exec(html);


var description = /

[\s\S]*?[\s\S]*?/gim.exec(html);


邮箱匹配

邮箱格式:账号@域名

其中:

帐号只能以字母或数字开头,可包含字母、数字、下划线、中划线和点(允许多个下划线、中划线和点相连);

域名分为域名前缀和域名后缀;

域名的前缀只可以是字母、数字和中划线,且中划线不能开头、不能结尾、不能连续2个中划线在一起;

域名的后缀只可以是小数点和字母,且必须是点开头,不能点结尾,不能连续2个点连在一起。

无论是账号还是域名都不区分大小写,也就是无论大小写最终多会转化成小写,所以在校验的时候大写是合法的,需要把大写考虑进去。

以下邮箱规则在网易邮箱亲测过合法。

合法的邮箱:

123---@qq.com

123...@qq.com

123---@qq.com

aBc.def@qq.com

abc@qQ-bb.com

不合法的邮箱:

-abc@qq.com

abc@qq--bb.com

abc@-qq.com

abc@qq..com

abc@qq.com.

abc@qq.123com

我的答案(2个答案都可以):

/^[a-zA-Z\d][\w-\.]*@([\da-zA-Z](-[\da-zA-Z])?)+(\.[a-zA-Z]+)+$/g.test('teAte@cdn-test.qq.com.cn')

/^[a-zA-Z\d][\w-\.]*@[\da-zA-Z]+(-[\da-zA-Z]+)*(\.[a-zA-Z]+)+$/g.test('teAte@cdn-test.qq.com.cn')

网上答案(都不完全准确):

/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/

/^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/

URL地址匹配

下面这个还不完全正确,有待完善:

var result = /^(https?:)\/\/([^\/:]+?)(:\d{1,5})?(\/[^\?]*)(\?[^#]+)?(#.*)?$/g.exec(location.href);

result[1] == location.protocol

result[2] == location.host

result[3] == location.port

result[4] == location.pathname

result[5] == location.search

result[6] == location.hash

ExpressionFactory 解析表达式_字符串