js中的正则表达式

正则表达式等名称并不能让人一下就能明白是什么意思,我认为称之为 规则表达式 更为合理,就是描述一个字符串规则的表达式。

语法

正则表达式的语法有两种,下面第一种语法稍显 怪异,但确实是合法的并且相对方便,推荐。

var reg = /pattern/modifiers;
//或者
var reg = new RegExp(pattern,modifiers);

↓使用

var a = /Hello/;
//或者
var a = new RegExp('Hello World');

var str = 'Hello World';
str.search(a); //返回0,这是 a 在 str 中匹配到的字符的位置

上面是一个匹配字符"Hello"的一个正则表达式。

修饰符

字符含义
g全局匹配
i忽略大小写
m匹配多行
var a = /a/;
var str = 'abcabc';
str.match(a); //['a'] str里面有两个a但是只匹配了一个,想要匹配字符串中全部符合表达式的字符串怎么办?

//g
var aGlobal = /a/g;
str.match(aGlobal); //['a', 'a'] 这样就匹配了str中所有符合的字符

//i
var strI = 'abcAbc';
strI.match(aGlobal); //['a'] 返回一个元素 'A' 没有被匹配到,因为字符的大小写不同,想要忽略大小写怎么办?

var aGlobalIgnore = /a/gi;
strI.match(aGlobalIgnore); //['a', 'A'] 这样大写的'A'也匹配到了

//m
var str2 = 'abc\nabc';
str2.match(aGlobal);  //['a', 'a']

var aNM = /^a/g;  // ^是匹配位置的元字符表示一行开始位置,也就是说"bacd"中的"a"是不会被匹配的,只会匹配出现在行首位置的"a"
str2.match(aNM); //['a'] 默认情况下一个字符串无论字符串内有没有"\n"换行符都只有一个字符串开始位置^和一个字符串结束位置$,但是如果使用了"m"修饰符那么"\n"就会使每一行都有一个"^"开始位置和一个"$"结束位置

var aMultiline = /^a/gm;
str2.match(aMultiline); //['a', 'a'] 这样两行的开头的a都被匹配到了

补充:m必须作用于 \n 的字符串并且正则表达式中要有 ^ 或者 $ 的位置匹配规则才会有意义。

量词/重复

在正则表达式中可以很轻松的表示重复

var a = /a/;
var str = 'aaaba';
str.match(a); // ['a'] 字符串中有3个连续的a,怎么匹配这3个连续的a呢?
var a1 = /a{3}/; // ['aaa'],{3} 表示前面的模式重复3次,也可以写成 --> /aaa/ 但是要是100个怎么办???
符号含义
+匹配1次或者无数次
*匹配0次或者无数次
?匹配1次或者0
{n}匹配重复n次
{n,}匹配至少重复n次
{m, n}匹配>=m次,<=n次

贪婪与懒惰

当正则表达式进行匹配的时候一次会尽量匹配最多符合的字符,这就是所谓的贪婪。

var a = /a+/;
var str = "aaa";
str.match(a); // ["aaa"] 这里返回了正则能匹配的所有的字符,并不是在遇到第一个合适的就结束匹配, * 元字符会尽可能多的匹配,体现了正则的贪婪模式。

var a1 = /a+?/; // 这个新增的?就告诉正则要尽可能少的匹配字符,开启了懒惰模式
str.match(a1);	// ["a"] 这就只返回一个符合条件的结果,即使后面还有符合条件的也不会继续匹配
字符含义
*贪婪
+贪婪
{n, }贪婪
*?懒惰
+?懒惰
{n,}?懒惰

元字符

元字符是在正则表达式中有特殊意义的字符,所以他们不能表示自身,在正则表达式中如果要匹配他们则需要转义(见下文)。

现在要匹配一个字符串(adef2ew)中出现的一个数字,但是却并不知道 具体 是哪个数字,这时候我们就可以使用元字符(\d)来表示我们需要寻找的这一类字符(数字)。

var a = /\d/;
var str = 'adef2ew';
str.match(a); // ['2']

元字符就是在正则表达式中有着特殊含义的一些字符,以下是一些元字符(不是全部,本文中所有表格中的字符都是元字符),可以在正则表达式中发挥难以想象的威力。

符号含义
.(点)除了换行符的所有字符
\wa-z、A-Z、0-9,以及下划线, 包含 _ (下划线) 字符
\s制表符,回车等空白符
\d数字

位置匹配

还有可能我们要匹配一个单词(hello),但是我们并不想匹配(helloword)中的(hello),我们要这样的(hello word),它是一个单词,并不是一个单词的组成部分。我们可以这样:

var a = /\bhello\b/;
var str = 'helloworld';
str.match(a); // null

var str1 = 'hello world';
str1.match(a); // ['hello']

//上面用到了\b表示是单词的边界(开头或者结尾),也就是该元字符匹配了一个位置。

现在想要匹配一个字符串(.jpg),但是要求是这个字符串必须要在一个字符串的结尾,不能在字符串的中间。

var a = /\.jpg/; // . 需要用 \ 转义否则表示的不是 . 字符本身。
var str= 'hehe.jpghaha';
str.match(a); // ['.jpg'] 这里匹配到了,但是我们要的不是这样的,这个字符串并没有出现在所在字符串的结尾

var str1 = 'hehehaha.jpg';
var a1 = /\.jpg$/; // $ 表示的是匹配字符串结尾位置
str.match(a1); // null 并没有获取到匹配的内容
str1.match(a1); // ['.jpg'] 获取到了
符号含义
\b单词边界(什么是边界?)
$结束
^开始

转义

如果我要匹配的字符串中有上面的元字符表示的字符串怎么办?这时就需要用到转义。

符号含义
\转义特殊字符
var a = /./; //这个表示的是匹配任意除了换行的字符
var b = /\./; //这个才是表示匹配**.**字符
var c = /\\/; // 匹配 \ 字符串

在[](下文的集合)中的元字符可转义也可不转义,主要取决于该字符有没有歧义。

集合

有时我们只想匹配a-z的小写字母并不想匹配大写的或者其他字符怎么办?

var a = /[a-z]/; //这就表示匹配一个字母必须是小写字母
var b = /[1-9]/; //简写如1,2,3,4,5,6,7,8,9可以写成如下形式
var d = /[1-9a-zA-Z]/; //表示数字和字母的集合

还有可能我们可以匹配所有字符但是除了所有小写字母

var a = /[^a-z]/;
var str = 'a';
str.match(a); //null

var str1 = '1a';
str1.match(a); //['1']

以下是一些集合的含义。

字符含义
[123]匹配1,2,3中任意一个
[1-9a-zA-Z]匹配数字小写字母和大写字母
[^a-z]匹配除了小写字母的所有字符

分支

分支可以理解为正则表达式的集合,回想一下分组 [abc]表示的是 abc ,但是如果我想表示 a11a11 (1是数字不是字母l) 这个怎么办?

[a11a11]这样吗? 这样好像 不正确。。。

使用集合并不能做到,因为集合只是表示的单个字符的集合,而上面的问题涉及的并不只有一个字符,而是一个表达式才能完整表示的,所以这时就需要用到分支(|),** 表示或的意思 ** 。

var a = /a1|1a|11/;
var str = 'a1';
str.match(a); // ['a1']

var str1 = '1a';
str1.match(a); // ['1a']

var str2 = '11';
str2.match(a); // ['11']

所以分支可以表示表达式的集合。

分组 / 子表达式

str.replace( search, ‘$1’, );
在前面提到的重复可以轻松实现 a,aa,aaa,的匹配(/a+/),但是如果要是匹配的超过了一个字符的重复如he,hehe,hehehe,hehehehe这样的怎么办呢?

提醒:通过这个问题也能知道 ** 重复只能重复之前的一个字符 **。

var a = /he+/; //这样吗?但是这个匹配的是 he,hee,heee...这种字符,所以不行。
var a1 = /he|hehe|hehehe/ //这样吗? 这也太麻烦了,并且还不是一个完整的集合
var str = 'zhouheheabcdefg';
str.match(a1); //['he'] 这样确实可以实现一部分

var a2 = /(he)+/;
var str1 = 'zhouheheheheabcdefg';
var str2 = 'zhouheheheheheheabcdefg';
str1.match(a2); // ['hehehehe', 'he']
str2.match(a2); // ['hehehehehehe', 'he']
//这里正确的匹配了字符串中所有的 he 的重复,无论重复几次

##分组的捕获
\1 来引用表达式中第一个分组(子表达式)
但是这个有一个弊端就是\1是相对位置,如果前面插入了一个分组(子表达式)那就完蛋了。

匹配 重复的单词,比如 ha ha

var a = /\b(\w+)\b\s+\1\b/; // 这里的 \1 就是对分组 (\w+) 的引用
var str  = 'ha ha abcd';    // 这里的 ha ha符合条件所以被捕获
var str2 = 'ha he abcd';    // 这里 ha 后面的单词是 he 不一样所以没有被捕获
str.match(a);  // ["ha ha", 'ha']
str2.match(a); // null

##非捕获组

正则表达式匹配成功的不一定会捕获,比如位置元字符 ^,$,\b等和零宽断言都是匹配了但是并不捕获。

var a = /(?:a)/;
var str = 'abcdefg';
str.match(a);  // ['a'] 按照下面第一个注解说的 () 分组的内容会被捕获到结果的对应的组号处 (?:a) 是一个分组。但是结果却是没有捕获,因为(?:) 的意思就是不捕获到分组,并且不会分配组号。

var b = /(a)/;
str.match(b);  // ['a', 'a']

分组就是把一个规则看成一个整体,整个正则表达式就是一个大的分组,在这个大的分组中可以有很多子分组。

注:上面用match方法捕获有分组的正则表达是的时候返回值([‘hehehehe’, ‘he’])中不止一个元素,第二个元素就是正则表达式中分组所匹配捕获的内容。(上面一段话整个表达式就是一个分组,所以整个表达式匹配捕获的内容在最前面,后面是子分组所捕获的内容,子分组的顺序就是在数组中捕获内容的顺序)

#常用分组语法

符号含义
(exp)匹配 exp ,并捕获文本到自动命名的组里
(?:exp)不捕获文本并且不给该分组分配组号[1]
(?=exp)匹配 exp 后面的位置
(?<=exp)匹配 exp 前面的位置
(?!exp)匹配后面不是 exp 的位置
(?<!exp)匹配前面没有exp 的位置

正则中的前和后怎么理解?

在匹配一个字符串的时候是从左往右匹配的,所以:

var a = /g/;
//         ---匹配方向--->
// 		   后---------->前
var str = 'abcdefghijklmn';
// 和正常认为的前和后不一样

断言

零宽度正预测先行断言

需要匹配一个 y,并且这个 y 的前面要有 ing,这样的要求上面的知识就不能做到。

var a = /y(?=ing)/; // 匹配 y 前面有 ing 的 y
var str = 'ying';   // y 的前面有 ing,所以 y 被匹配
str.match(a);  // ['y'] 这里匹配到了结果
var str2 = 'yang';  // y 的前面是 ang,不是 ing, 所以 y 没有被匹配
str2.match(a); // null  这里的字符串不满足条件所以没有匹配到结果

零宽度正回顾后发断言

需要匹配一个 ing,并且这个 ing 的后面要有 y

var a = /(?<=y)ing/; // 匹配 ing 后面有 y 的 ing
var str = 'ying';    // ing 后面有 y,所以 ing 被匹配
str.match(a);  // ["ing"]
var str2 = 'aing';   // ing 后面是 a,不是 y,所以 ing 没有被匹配
str2.match(a); // null

零宽度负预测先行断言

需要匹配一个 y,并且这个 y 的前面不能有 ing。

var a = /y(?!ing)/; // 匹配 y 前面没有 ing 的 y
var str = 'yang';   // y 前面是 ang,不是 ang,所以 y 被匹配
str.match(a);  // ['y']
var str2 = 'ying';  // y 前面是 ing,所以 y 没有被匹配
str2.match(a); // null

零宽度负回顾后发断言

需要匹配一个 ing,并且这个 ing 的后面不能有 y。

var a = /(?<!y)ing/; // 匹配 ing 后面没有 y 的 ing
var str = 'aing';   // ing 后面是 a,不是 y,所以 ing 被匹配
str.match(a);  // ['ing']
var str2 = 'ying';  // ing 后面是 y,所以 ing 没有被匹配
str2.match(a); // null

对断言名称的理解:

零宽度:只匹配不捕获
正:有
负:没有
预测,先行:向前
回顾,后发:向后

案例

验证邮箱是否合法

// 邮箱格式: 用户名 @ 域名
var a = /^[1-9a-zA-z_\-]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$/;
var str = 'haha@qq.com';
str.match(a); // ["haha@qq.com", ".com"]

格式化千分位

将123456789格式化成123,456,789

var a = /(\d)(?=(\d{3})+$)/g;
var str = '123456789';
str.replace(a, '$1,'); //123,456,789

参考

正则表达式30分钟入门教程—墙裂推荐阅读

菜鸟教程正则表达式教程

正则表达式中的m修饰符1

正则表达式中的m修饰符2

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值