js正则理论与实践

学习正则表达式的时候,可以说正则并不是很难,但是就是老记不住语法规则,相信大家跟我也有同样的发现吧,但是没办法,需要耐心的东西只能多啃几遍了。

正则表达式的作用

  • 数据验证。
  • 复杂的字符串搜寻、替换。
  • 基于模式匹配从字符串中提取子字符串。

概述

正则表达式包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”)。

若要匹配这些特殊字符,必须首先转义字符,即,在字符前面加反斜杠字符 \**。
例如,若要搜索 “+”文本字符,可使用表达式 \+
但是大多数 特殊字符 在中括号表达式内出现时失去本来的意义,并恢复为普通字符。

构造函数

可使用字面量也可以用内置的构造函数:

var regex = new RegExp('xyz', 'i');
var regex = new RegExp(/xyz/i); 
var regex = /xyz/i; 复制代码

有一种情况要注意:就是如果正则中出现了反斜杠 “\” ,在用构造函数时创建正则对象时,要转义, 比如: re = new RegExp("\\w+") // 这里的 \ 要转义,re = /\w+/ // 这样就不需要转义;

用于模式匹配的String方法

  • String.search()
    参数:要搜索的子字符串,或者一个正则表达式。
    返回:第一个与参数匹配的子串的起始位置,如果找不到,返回-1
    说明:不支持全局搜索,如果参数是字符串,会先通过RegExp构造函数转换成正则表达式。如:

    var str="i am jay"; 
    var res=str.search("jay");
    console.log(res); //5
    console.log(str.search("hz"); //-1复制代码

  • String.replace(),个人认为是正则中最强大的api
    作用:查找并替换字符串。
    第一个参数:字符串或正则表达式,
    第二个参数:要进行替换的字符串,也可以是函数。
    用法:

当第二个参数是字符串时,替换文本中的$字符有特殊含义:

属性 描述
$1,$2,…,$99 匹配第 1-99 个 分组里捕获的文本
$& 匹配到的子串文本
$` 匹配到的子串的左边文本
$' 匹配到的子串的右边文本
$$ 美元符号

如:

'abc'.replace(/b/g, "{$$$`$&$'}")
// 结果为 "a{$abc}c",即把b换成了{$abc}复制代码
当第二个参数是函数时,如:

"1234 2345 3456".replace(/(\d)\d{2}(\d)/g, function (match, $1, $2, index, input) {
        console.log([match, $1, $2, index, input]);
        });    
// => ["1234", "1", "4", 0, "1234 2345 3456"]
// => ["2345", "2", "5", 5, "1234 2345 3456"]
// => ["3456", "3", "6", 10, "1234 2345 3456"] 复制代码

  • String.match()
    参数:要搜索的子字符串,或者一个正则表达式。
    返回:一个由匹配结果组成的数组与是否有修饰符g有关

非全局检索:如果没有找到任何匹配的文本返回null;否则数组的第一个元素是匹配的字符串,剩下的是小括号中的子表达式,即a[n]中存放的是$n的内容。非全局检索返回三个属性:length 属性;index 属性声明的是匹配文本的第一个字符的位置;input 属性则存放的是被检索的字符串 string。

全局检索:设置标志g则返回所有匹配子字符串,即不提供与子表达式相关的信息。没有 index 属性或 input 属性。

如:

var string = "2017.06.27";
var regex1 = /\b(\d+)\b/;
var regex2 = /\b(\d+)\b/g;
console.log( string.match(regex1) );
console.log( string.match(regex2) );
// => ["2017", "2017", index: 0, input: "2017.06.27"]
// => ["2017", "06", "27"] 
复制代码

  • String.split(),个人经常用到的一个api,简单实用
    作用:把一个字符串分割成字符串数组,改变原字符串,与数组方法join对应
    参数:正则表达式或字符串,可以有第二个参数,表示结果数组的最大长度 ,并且当正则使用分隔符时结果数组中是包含分隔符的。
    返回:子串组成的数组。

RegExp的方法

  • RegExpObject.exec(),比match强大
    参数:字符串。
    返回:

非全局检索:与String.macth()非全局检索相同,返回一个数组或null。

全局检索:尽管是全局匹配的正则表达式,但是exec方法只对指定的字符串进行一次匹配。但是可以反复调用来实现全局检索。在 RegExpObject 的lastIndex 属性指定的字符处开始检索字符串;匹配后,将更新lastIndex为匹配文本的最后一个字符的下一个位置;再也找不到匹配的文本时,将返回null,并把 lastIndex 属性重置为 0。

var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
var result;
while ( result = regex2.exec(string) ) {
console.log( result, regex2.lastIndex );
}
// => ["2017", "2017", index: 0, input: "2017.06.27"] 4
// => ["06", "06", index: 5, input: "2017.06.27"] 7
// => ["27", "27", index: 8, input: "2017.06.27"] 10 
复制代码
  • RegExpObject.test(),匹配字串
    参数:字符串。
    返回:true或false。

  • RegExpObject.toString()
    返回:字符串

这两个较简单,就不多讲了。

字符

| 指示在两个或多个项之间进行选择。类似js中的或,又称分支条件
/ 正则表达式模式的开始或结尾。
\ 反斜杠字符,用来转义。
- 连字符 当且仅当在字符组[]的内部表示一个范围,比如[A-Z]就是表示范围从A到Z;如果需要在字符组里面表示普通字符-,放在字符组的开头或者尾部即可。

. 匹配除换行符 \n 之外的任何单个字符。
\d 等价[0-9],匹配0到9字符。
\D 等价[^0-9],与\d相反。                                                                                                \s匹配空白符。与\S相反

\w 与以下任意字符匹配:A-Z、a-z、0-9 和下划线,等价于 [A-Za-z0-9]。

\W 与\w相反,即 [^A-Za-z0-9]

限定符(量词字符)

显示限定符位于大括号 {} 中,并包含指示出现次数上下限的数值;*+? 这三个字符属于单字符限定符

{n} 正好匹配 n 次。
{n,} 至少匹配 n 次。
{n,m} 匹配至少 n 次,至多 m 次。
* 等价{0,}
+ 等价{1,}
? 等价{0,1}

注意:

  • 显示限定符中,逗号和数字之间不能有空格,否则返回null!
  • 贪婪量词*+:javascript默认是贪婪匹配,也就是说匹配重复字符是尽可能多地匹配。
  • 惰性(最少重复匹配)量词?:当进行非贪婪匹配,只需要在待匹配的字符后面跟随一个?即可。如:

    var reg = /a+/;
    var reg2 = /a+?/;
    var str = 'aaab';
    str.match(reg); // ["aaa"]
    str.match(reg2); // ["a"]复制代码

定位点(锚字符、边界)

^ 匹配开始的位置。将 ^ 用作括号[]表达式中的第一个字符,则会对字符集求反。
$ 匹配结尾的位置。
\b 与一个字边界匹配,如er\b 与“never”中的“er”匹配,但与“verb”中的“er”不匹配。
\B 非边界字匹配。

标记

  • 中括号[] 字符组;标记括号表达式的开始和结尾。
    [...] 匹配方括号内任意字符。很多字符在[]都会失去本来的意义:[^...]匹配不在方括号内的任意字符;[?.]匹配普通的问号和点号。
    注意:反斜杠字符 \[]中仍为转义字符。若要匹配反斜杠字符,请使用两个反斜杠 \\
    另外不要滥用字符组这个失去意义的特性,比如不要使用[.]来代替\:转义点号,因为需要付出处理字符组的代价

  • 大括号{} 标记限定符表达式的开始和结尾。

  • 小括号() 标记子表达式的开始和结尾,主要作用是分组,对内容进行区分。

(模式) 可以记住和这个模式匹配的匹配项(捕获分组)。不要滥用括号,如果不需要保存子表达式,可使用非捕获型括号(?:)来进行性能优化
(?:模式) 与模式 匹配,但不保存匹配项(非捕获分组)。
(?=模式) 零宽正向先行断言,要求匹配与模式 匹配的搜索字符串。 找到一个匹配项后,将在匹配文本之前开始搜索下一个匹配项;但不会保存匹配项。
(?!模式) 零宽负向先行断言,要求匹配与模式 不匹配的搜索字符串。 找到一个匹配项后,将在匹配文本之前开始搜索下一个匹配项;但不会保存匹配项。

可能会有点难懂,不过没关系,来几个例子就知道啥意思了

如下为捕获和非捕获分组区别:

'13588884444'.replace(/(\d{3})(\d{4})(\d{4})/g, '$1****$3')
//=>  "135****4444"
'13588884444'.replace(/(\d{3})(?:\d{4})(\d{4})/g, '$1****$2')
//=>  "135****4444"
复制代码

先行断言(?=模式):x只有在y前面才匹配,必须写成/x(?=y)/。 解释:找一个x,那个x的后面有y。
先行否定断言(?!模式): x只有不在y前面才匹配,必须写成/x(?!y)/。 解释:找一个x,那个x的后面没有y。

如:对其后紧跟 "all" 的 "is" 进行全局搜索:

var str="Is this all there is";
var patern=/is(?= all)/g;
console.log(str.match(pattern));  //["is"]复制代码

var s = "i am jay";
var p = /j(?!ay)/g;
s.match(p)
//null
复制代码

  • 反向引用:主要作用是给分组加上标识符\n。
    \n 表示引用字符,与第n个子表达式第一次匹配的字符相匹配。

反向引用的例子,如要写一个正则支持匹配如下三种格式: 

2016-06-12
2016/06/12
2016.06.12  

最先可能想到的正则是:

var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/; 
复制代码

  其中 / 和 . 需要转义。虽然匹配了要求的情况,但也匹配 "2016-06/12" 这样的数据。 

假设我们想要求分割符前后一致怎么办?此时需要使用反向引用:  

var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false 
复制代码

注意里面的 \1,表示的引用之前的那个分组 (-|\/|\.)。不管它匹配到什么(比如 -),\1 都匹配那个同样的具体某个字符。
我们知道了 \1 的含义后,那么 \2 和 \3 的概念也就理解了,即分别指代第二个和第三个分组。  

优先级顺序

  1. \ 转义符
  2. (), (?:), (?=), [] 括号和中括号
  3. *、+、?、{n}、{n,}、{n,m} 限定符
  4. 任何元字符^、$、\ 定位点和序列
  5. | 替换

实践

看完介绍内容后可以看看以下的例子,欢迎大佬们在评论区给出答案指定!!!

  1. 匹配 16 进制颜色值 
 要求匹配:
#ffbbad
#Fc01DF
#FFF
#ffE  


2.匹配时间  

以 24 小时制为例。
要求匹配:
23:59
02:07  

3.匹配日期 

比如 yyyy-mm-dd 格式为例。
要求匹配:
2017-06-10 

4.匹配 id 

 要求从

<div id="container" class="main"></div>
提取出 id="container"。

5.数字的千位分隔符表示法 

比如把 "12345678",变成 "12,345,678"。
可见是需要把相应的位置替换成 ","。  

6.字符串 trim 方法模拟

trim 方法是去掉字符串的开头和结尾的空白符。 

7.匹配成对标签 

要求匹配:
<title>regular expression</title>
<p>laoyao bye bye</p>
不匹配:
<title>wrong!</p> 

 8.将形如"a=1&b=2&a=3&b=4" 的字符串压缩为"a=1,3&b=2,4" ,(用replace)

9.test整体匹配某个字符串

10.IPV4 地址(优先级知识点) 

欢迎大佬们在评论区给出答案指定!!!,如想知道解析的也可在评论区提出。






猜你喜欢

转载自juejin.im/post/5c31a330e51d45522d17260b