本文力求解释清楚JS中正则表达式的用法,尽最大努力来帮助大家吃透正则。
本文参考学习自:JavaScript权威指南、JavaScript高级程序设计两书;
我有十足的把握,你阅读完后一定会有许多收获;
本文加上了一些个人拙见和理解,纯属抛砖引玉,若有不妥之处,千万指出,共同学习;
从创建正则对象讲起
下面两种方式创建的都是对象 —— 引用类型
// 两种方法是等效的
> let reg = /.at/i; // 字面量方式创建,常用
> let reg2 = new RegExp(".at", "i"); // 构造函数创建
从上面可以看出,正则表达式可分为两部分 —— 模式和标记,即 /pattern/flags ,下面从这两方面入手:
当然关于构造函数创建正则对象还有些不容忽略的细节,在弄懂了模式和标记后,才能更好理解,暂且搁置。
1,正则表达式之模式
所谓的模式不过是用于匹配搜索字符串的模板罢了,由一些元字符、直接量字符、JS定义的特殊字符
元字符:由特殊含义的字符
直接量字符:字母 数字 \o \t \n \v \f \r \xnnn \uxxx,特点就是不需要转义,可直接使用
特殊字符:\d \D \s \S \w \W,用于简化几类字符的表示
元字符
总的来说,有这些 () [] {} * + ? . ^ $ \ | / : ! = 下面先留个印象:
() 组合成独立单元 嵌套定义子模式 分组以便被后面引用前面匹配到的文本[按(排序],如\1
[] 范围
{} 匹配重复次数的范围,尽可能多的匹配
* 前面表达式出现次数 >=0 次,尽可能多的匹配
+ 前面表达式出现次数 >=1 次,尽可能多的匹配
? 前面表达式出现次数 0或1 次
^ 出现在开头,在[]中则表示取反
$ 出现在结尾
. 匹配除\n的任意单个字符
\ 用于转义元字符为普通字符
| 或
/ 用于表示正则,需要转义为普通字符
: 常用于组合其他元字符使用
! 常用于组合其他元字符使用
= 常用于组合其他元字符使用
莫慌,说来话长,下面再进行分类举例搞定!
1.1 用作锚点的元字符
即匹配搜索字符串的哪个位置,主要有:^ $ \b \B (?= ) (?! )
> let reg1 = /^cat/; // ^ 匹配搜索字符串的开始,比如可匹配:cat catch...
> let reg2 = /cat$/; // $ 匹配结束位置,可匹配:cat concat...
> let reg3 = /^cat$/; // 只能匹配字符串:cat
> let reg4 = /\bcat\b/; // \b 匹配ASCAII码单词的边界,而不会匹配可见的字符,可匹配:"It's a cat",不会匹配"It's a cate"因为cate不是一个单词
> let reg5 = /\B\u4f60\u597d\B/;// \B 匹配非ASCAII码字符组成的单词的边界,这两个Unicode字符是"你好",所以可匹配:"\u4f60\u597d\u4e16\u754c"(你好世界)
> let reg6 = /^(mail)(?= \:)/; // (?= \:) 必需匹配:字符,这里:是元字符所以需要用\转义,可匹配"mail:xxx@xxx.com",不可匹配"email:xxx@xxx.com"
> let reg7 = /Java(? !Script)/; // (?! Script)表示不能匹配Script,可匹配"JavaScripter",不可匹配"JavaScript"
1.2 用作表示字符类
以下都是匹配一类中的单个字符。
[ ] 匹配指定范围内的字符
[^ ] 匹配不在指定范围内的单符
. 匹配除\n外的所有字符
\d 匹配数字字符,等效于[0-9]
\D 匹配非数字字符,等效于[^0-9]
\w 匹配ASCAII码组成的单词
\W 匹配非ASCAII码组成的单词
\s 匹配空白符
\S 匹配非空白符
> let reg1 = /[ch]at/; // 可匹配"cat" "hat",不匹配"at"
> let reg2 = /[a-z]at/; // 可匹配范围:aat-zat,不可匹配"1at"
> let reg3 = /[^a-z]at/;// 可匹配"1at",不可匹配"cat"
> let reg4 = /.at/; // 可匹配"cat",不可匹配"\nat"
> let reg5 = /\d1/; // 可匹配"11",不可匹配"a1"
> let reg6 = /\D1/; // 可匹配">1",不可匹配"11"
> let reg7 = /\wll/; // 可匹配"hello",不可匹配"hill"
> let reg8 = /hi\W/; // 可匹配"\u4f60\u597d"你好,不可匹配"hihello"
> let reg9 = /that\s/; // 可匹配"that\t",不可匹配"that's"
> let reg10= /that\S/; // 可匹配"that's",不可匹配"that\t"
1.3 重复匹配
以下会贪婪匹配,即尽可能多地匹配前一项子表达式
* 匹配次数 >= 0 等价于 {0, }
+ 匹配次数 >=1 等价于 {1, }
? 匹配次数为 0或1 等价于 {0,1}
{n} 匹配n次
{n,} 匹配至少n次
{n,m} 匹配至少n次,但不超过m次
> let reg1 = /[0-9]*/; // 可匹配"123a" ""
> let reg2 = /[a-z]+/; // 可匹配"abc1",不可匹配"1234"
> let reg3 = /[A-Z]?/; // 可匹配"A1" "1" ""
> let reg4 = /[0-9]{3}/; // 可匹配"123" "a456",不可匹配"12" "a12"
> let reg5 = /[a-z]{3,}/; // 可匹配"12345",不可匹配"12" "abc12"
> let reg6 = /[A-Z]{2,4}/; // 可匹配"12AI" "AUTO,不可匹配"ECMAScript"
有时,我们要求尽可能的少匹配,只需要在重复匹配的符号后面加上?符号,比如:
> let reg7 = /[0-9]*?/;
> let reg8 = /[a-z]+?/;
> let reg9 = /[A-Z]??/;
> let reg10= /[0-9]{2,4}?/;
1.4 选择、分组、引用字符
这里较难理解!需要特别说明
| 选择左右表达式中的一个进行匹配,但优先尝试左表达式进行匹配;
()主要作用有3种:组成独立单元:
便于和重复匹配等修饰符连用,这样可以对一个独立部分进行重复匹配
定义子模式
这样使得()可以嵌套,而且嵌套()内的模式所匹配的字符串可以抽取出来,更加容易得到所需的部分
生成匹配文本的组索引
为了便于后面的模式引用,特别注意,该引用不是指向该子模式,而是指向模式实际匹配到的文本,组索引的排序是依据左括号 ( 从1开始编号的,而且不关心()是否嵌套。
(?: ) 只表示组合
不参与组索引的排序,所以后面的模式是无法引用到该子模式实际匹配到的文本
\N 表示引用前面子模式实际匹配到的文本,N为索引值
// 选择
> let reg1 = /[a-z]|[A-Z]/; // 可匹配"a" "z",不可匹配"1"
// 组成独立单元
> let reg2 = /([a-z]\d)+hi/; // 可匹配"a1hi",不可匹配"a123hi" "hi"
> let reg3 = /[a-z]\d+hi/; // 可匹配"a123hi",不可匹配"ahi",省略()则只能匹配前面最近的模式
// 嵌套定义子模式
> let reg4 = /^(([ab]\d){1,2})/;// 可匹配"a1a1" "a1b2c3d4",不可匹配"1a1b2"
// \N表示索引前面对应的子模式
> let reg5 = /(['"])[^'"]\1/; // 可匹配对应引号且引号不嵌套'a',不可匹配'a" '"a'
// 只组合而不参与索引
> let reg6 = /(?: ['"])[^'"]\1/;//始终都是无效的,因为\1无指向,(?: ['"])不参与索引
2,正则表达式之标记
有这几个标记:g i m y u s
g 全局匹配,即每次匹配会在上次成功匹配后的位置继续匹配,而不再从头开始匹配
i 忽略大小写
m 多行匹配,此时,$可匹配字符串的首尾部,还可匹配每行的首尾部
y 每次在正则对象的lastIndex位置上匹配
u 启用Unicode匹配
s 表示.可以匹配任何字符,含 \n \r
回到前面搁置的问题
关于构造函数创建正则对象时,必须注意的细节。
> let reg = /.at/i; // 字面量方式创建,常用
> let reg2 = new RegExp(".at", "i"); // 构造函数创建,第一个参数表示正则表达式,第二个参数表示标记
1,构造函数的参数不需要再使用斜线/来创建正则对象;
2,只要将字面量创建方式中的模式和标记部分转换为字符串形式即可;
3,构造函数的两个参数必须传字符串类型数据,因此,这就会带来一些问题:字符串中可以放转义字符:\n \t \v \r…
模式中可以放特殊字符(\d \D \s \S \w \W)和元字符4,那么,模式的字符串形式中如何表示普通字符*和\n呢?
1,对于元字符:
分析:
对于普通意义的元字符而言,用\转义即可作为普通字符,在字符串形式中,还需要进行二次转义,即再用\转义一次(因为\不仅作为正则的转义符号,还作为字符串的转义符号)
对于模式中有特殊意义的转义字符(\n \t \v….),其中的 \ 不需进行转义,因为 \本身就是用于表达特殊意义的字符
结果:* 的字符串形式为 \\*2,对于转义字符:
分析:\n 中的 \ 是正则的元字符,则需要转义,转义后变为 \\n,同样,在字符串形式中还有进行二次转换:\\\\n,不必惊讶,事实确实如此!
结果:\n 的字符串形式为 \\\\n可见特殊字符的转换和转义字符的转换是一样的,只需要关注模式中 \的转义。
你可能注意到了,上面讨论的是模式的字符串形式如何表示,并没有涉及标记的字符串表示,其实,因为标记(gimyus)中并不含元字符或特殊字符,所以根本没必要进行转义。
下面的几个实例可以帮助你更好的理解:
字面量模式 | 对应字符串 |
---|---|
/\[bc\]at/ | “\\[bc\\]at” |
/\.at/ | “\\.at” |
/name\/age/ | “name\\/age” |
/\d.\d{1,2}/ | “\\d.\\d{1,2}” |
/\w\\hello\\123/ | “\\w\\\\hello\\\\123” |
本文只是介绍了一些正则表达式本身的性质,还没有涉及正则的另一面性质 —— 对象
如果不当之处,尽管斧正,尽情重喷!
了解更多:JS中的正则表达式(下)