目录:
- 1、求教正则表达式如何匹配
- 2、正则表达式[能随意匹配出任何想要的东西]
- 3、正则表达式字符匹配
- 4、正则表达式位置匹配攻略【转】
求教正则表达式如何匹配
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
1.当模式第二位是*时,且第一位字符匹配,分三种匹配模式:
1.把*当做0,就是忽略掉前两位X*,继续做匹配;
2.把*当做是1,字符串后移一位,模式后移两位跳过这个*,继续匹配;
3.把*当做1,即可能字符串的下一位与上一位相同,所以做匹配。如果不匹配,模式后移两位。
2.当模式第二位不是*时,如果第一位匹配则全部后移一位进行匹配,否则直接返回false。
正则表达式[能随意匹配出任何想要的东西]
一. 正则表达式的基础知识:
1.字符串的组成
对于字符串”123“而言,包括三个字符四个位置。如下图所示:
2.占有字符和零宽度
正则表达式匹配过程中,如果子表达式匹配到东西,而并非是一个位置,并最终保存到匹配的结果当中。这样的就称为占有字符,而只匹配一个位置,或者是匹配的内容并不保存到匹配结果中,这种就称作零宽度,后续会讲到的零宽度断言等。占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。
3.控制权和传动
正则表达式由左到右依次进行匹配,通常情况下是由一个表达式取得控制权,从字符串的的某个位置进行匹配,一个子表达式开始尝试匹配的位置,是从前一子表达匹配成功的结束位置开始的(例如:(表达式一)(表达式二)意思就是表达式一匹配完成后才能匹配表达式二,而匹配表达式二的位置是从表达式一的位置匹配结束后的位置开始)。如果表达式一是零宽度,那表达式一匹配完成后,表达式二匹配的位置还是原来表达式以匹配的位置。也就是说它匹配开始和结束的位置是同一个。
举一个简单的例子进行说明:正则表达式:123
源数据:123
讲解:首先正则表达式是从最左侧开始进行匹配,也就是位置0处进行匹配,首先得到控制权的是正则表达式中的“1”,而不是源数据中的“1”,匹配源数据中的“1”,匹配成功,将源数据的“1”进行保存到匹配的结果当中,这就表明它占有了一个字符,接下来就将控制权传给正则表达式中的“2”,匹配的位置变成了位置1,匹配源数据中的“2”,匹配成功,将控制权又传动给了正则表达式的“3”,这时候匹配的位置变成了位置2,这时候就会将源数据中的“3”进行匹配。又有正则表达式“3”进行传动控制权,发现已经到了正则表达式的末尾,正则表达式结束。
二、元字符
三、转义字符
如果你想要得到元字符本身的话需要使用“”来取消这些元字符的特殊意义。
四、字符类
首先字符类使用“[]”包起来的,例如以下这个例子:(大小写要区分)
①[aeiou]则表示匹配任意一个英文元音字母(这个仅仅是匹配一个,也就是说你如果匹配了a这个整个正则表达式就已经结束了,这里面的逻辑表示的是“或”的意思),再看这个例子[.!?]表示匹配.或者?或者!
②[a-zA-Z0-9]这个正则表达式表示的是匹配a到z的任意一个小写字母,或者是A到Z的任意一个字母,或者是数字0到9任意一个.
五.重复(MSDN上称作是限定符)
六.分支条件
其实正则表达式中的分支条件,就指的是有几种规则:用“|”把不同的规则分开
来看下例子:
①0d{2}-d{8}|0d{3}-d{7}:匹配两种以连字号分隔的电话号码;一种是三位区号8位本地号(例如:010-12345678),另外一种规则则是4位区号7位本地号(例 如:0315-8834524)
②d{5}-d{4}|d{5}:需要注意的是使用分支条件是一定要注意分支条件的顺序,如果改成d{5}|d{5}-d{4}这个样子的话,那么只会匹配五位数字而不会匹配后面的四位数字(例如:我们利用第二个匹配12345-1234,它只会匹配12345,原因是:正则表达式是从左到右依次匹配,如果满足了某个分支的话它就不会再管其他分支了)
七.分组
你可以使用小括号()来指定字表达式:(d{1,3}){3}d{3}:这个正则表达式的意思就是把我们分组的小括号里面的东西重复三次,也就是说我们至少匹配3个最多匹配9个数字,后面再加上三个数字。
八.反义字符
九.反向引用
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。但是其实分组号不是这么简单:
•分组0对应整个正则表达式
•实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
•你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.
通过上面三条讲述我们可以清楚地知道分组的方式是怎样的,其实意思就是首先我们先对没有为组进行命名的组进行分配组号(从左到右依次次分配),然后再对分配组号的组进行分配组号(使用(?组名)方式显示分配组名称),如果你想剥夺某一个组的组号可以采用(?:exp)这种方式进行剥夺,也就是不给他分配组号,可以理解为跳过此组。
正则表达式:(?3)(1)(2)(?565)
匹配文本:312565
匹配结果表明首先0号组的是匹配的整个表达式,匹配1号组名的则是1,匹配2号组的是2,匹配3号组的就是命名为work组名的3,匹配4号组的则是匹配命名为SmallDing组名的565,显然可以看到分配组号就是按照以上的规则来分配。
说到了反向引用我们来看一下反向引用是啥概念,我们前面已经详细讲解了组号的分配,那么反向引用则用于重复搜索前面某个分组匹配的文本,例如1代表分组1匹配的文本
请看下面的例子:
正则表达式:(1)(2)(3)2则表示匹配123且在此匹配组号为2的内容也就是再次匹配2
匹配文本:1232
正则表达式:(?333)(?222)(?:d{3})该正则表达式代表的是显示为匹配333的组分配组名为work,显示为匹配222结果的组分配组名为222,但是如果匹配3位数字这个组已经取消了组号,所以该组号是没有的,也就是整个正则表达式是第一个组号为0,首先将所有未命名的组进行分配组号,而只有一个(?:d{3})这个没有分配组名,但是它却将组号进行取消了,所以组号不会给它分配。
源文本为:333222123
那现在我们就来讲一下零宽断言和负零宽断言
常见的几种分组方法
零宽度断言
1.(?=exp):也叫零宽度正预测先行断言,它匹配自身出现的位置后面能匹配表达式exp
例如:bw+(?=ingb)则这个正则表达式就是匹配一ing结尾的单词,但是不包含ing,这个零宽度正预测先行断言可以这样理解,我们就以上面的正则表达式作为例来进行讲解,首先我们肯定是匹配源文本为doing它会先匹配d的时候它会瞻仰一下后面跟的是不是ing,如果不是就会继续往下走,匹配到第二个字符o它会预测(或瞻仰)下后面是不是ing如果是整个表达式就结束了,并且不匹配ing。而这个可以总结一句话就是匹配exp前面的东西
2.(?=exp):也叫零宽度正回顾断言,它匹配自身出现位置的前面匹配表达式exp,这句话听着很绕口,其实零宽度正回顾断言中解释说是自身出现位置这个自身出现位置是表示它匹配的文本,就比如说(?=Ding)d{3}这个正则表达式,这里的自身出现的位置仅仅是从开始匹配文本的时候也就是d{3}也就是主动权在这个d{3}的时候才是自身匹配的位置。举例说明源文本,比如匹配Din123,按照我们的常理理解的是数字123是自身匹配的位置,但是前面不是Ding所以匹配不成功,我们可以讲这个表达式理解为就是以exp为开始的正则表达式但是不包含exp,意思就是匹配exp后面的东西。
负向零宽断言 :(可以和上面的进行对比来学哦!这个表达式的是否定的)
1.(?!exp):也叫零宽度负预测先行断言,断言此位置的后面跟的不能匹配表达式exp,
例如:d{3}(?!123):正则表达式的含义表达了前面匹配的是三个数字,匹配的位置就是当前匹配的这三个数字后面跟的不能是123。
十.平衡组
接下来我来讲一下平衡租的原理,在上面我们做下了铺垫,也就是说我们在第六节的时候提出来了一系列问题,是不是感觉一头雾水,没关系的,到了这一节终于守得云开见月明了,听过本章节的学习我相信你们会对上面的问题进行一个详细合理的回答!OK,Come On Baby!懂你们迫不及待心情,一定会说你咋这多废话呢,好,闲话少说,继续....
说到平衡组有些人就会想到分组,没错他们之间是有联系的,也就是我们前面所讲的分配组号的问题,那下面呢我们先引出语法,详细见下表
十一、贪婪与非贪婪
首先先说一下关于贪婪匹配和非贪婪匹配的一些基本概念,贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。
下面是一些限定符(限定符指定在输入字符串中必须存在上一个元素(可以是字符、组或字符类)的多少个实例才能出现匹配项)
贪婪匹配的限定符如下表所示:
非贪婪是在贪婪限定符后面多加一个“?”,如下表所示:
十二、贪婪匹配和非贪婪匹配原理
其实这节贪婪匹配与懒惰匹配应该放在重复后面讲,因为这个和重复有关系,那么下面详细介绍什么是贪婪匹配什么是非贪婪匹配,贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。非贪婪模式只被部分NFA引擎所支持。
(1).贪婪
来看一下匹配过程。首先将控制权交给“"”,由它来匹配第一个字符"匹配成功,将控制权转交给“.*”,这时候控制权掌握在了“.*”的手上,由于“*”是优先词量,在可匹配与不可匹配的情况下,优先尝试匹配,他就会尝试匹配第一字符R,匹配成功就会继续往下匹配,匹配第二字符e,匹配成功,继续向右匹配,直到匹配到结尾的“"”,匹配成功,再向后匹配时发现已经到结尾了,“.*”结束匹配将控制权转交给""","""发现已经到了源字符串的结尾,看有没有可供回溯的状态,将控制权给了“.*”,“.*”还回一个字符“x”,然后将控制权转交给“"”,来匹配后面的字符“"”,匹配成功正则表达式结束。这句表达式只进行了一次回溯。
(2).懒惰
源字符串:"Regex"
正则表达式:".*?"
看一下非贪婪模式的匹配过程。首先由第一个“"”取得控制权,匹配位置0位的“"”,匹配成功,控制权交给“.*?”。“.*?”取得控制权后,由于“*?”是忽略优先量词,在可匹配可不匹配的情况下,优先尝试不匹配,由于“*”等价于“{0,}”,所以在忽略优先的情况下,可以不匹配任何内容。从位置1处尝试忽略匹配,也就是不匹配任何内容,将控制权交给正则表达式最后的“””。
“"”取得控制权后,从位置1处尝试匹配,由“"”匹配位置1处的“R”,匹配失败,向前查找可供回溯的状态,控制权交给“.*?”,由“.*?”吃进一个字符,匹配位置1处的“R”,再把控制权交给正则表达式最后的“"”。
“"”取得控制权后,从位置2处尝试匹配,由“"”匹配位置1处的“e”,匹配失败,向前查找可供回溯的状态,重复以上过程,直到由“.*?”匹配到“x”为止,再把控制权交给正则表达式最后的“"”。
“"”取得控制权后,从位置6处尝试匹配,由“"”匹配字符串最后的“"”,匹配成功。
此时整个正则表达式匹配成功,其中“.*?”匹配的内容为“Regex”,匹配过程中进行了五次回溯。
正则表达式字符匹配
模糊匹配,有两个方向上的“模糊”:横向模糊和纵向模糊。
横向模糊指的是,一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。
其实现的方式是使用量词。譬如{m,n},表示连续出现最少m次,最多n次。
比如/ab{2,5}c/表示匹配这样一个字符串:第一个字符是“a”,接下来是2到5个字符“b”,最后是字符“c”。
纵向模糊指的是,一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种可能。
其实现的方式是使用字符组。譬如[abc],表示该字符是可以字符“a”、“b”、“c”中的任何一个。
比如/a[123]b/可以匹配如下三种字符串:"a1b"、"a2b"、"a3b"。
需要强调的是,虽叫字符组(字符类),但只是其中一个字符。例如[abc],表示匹配一个字符,它可以是“a”、“b”、“c”之一。
比如[123456abcdefGHIJKLM],可以写成[1-6a-fG-M]。用连字符-来省略和简写。
比如要匹配“a”、“-”、“z”这三者中任意一个字符,不能写成[a-z],因为其表示小写字符中的任何一个字符,可以写成如下的方式:[-az]或[az-]或[a-z]。即要么放在开头,要么放在结尾,要么转义。总之不会让引擎认为是范围表示法就行了。
纵向模糊匹配,还有一种情形就是,某位字符可以是任何东西,但就不能是"a"、"b"、"c"。
例如[^abc],表示是一个除"a"、"b"、"c"之外的任意一个字符。字符组的第一位放^(脱字符),表示求反的概念。
d就是[0-9]。表示是一位数字。
记忆方式:其英文是digit(数字)。
D就是[^0-9]。表示除数字外的任意字符。
w就是[0-9a-zA-Z_]。表示数字、大小写字母和下划线。
记忆方式:w是word的简写,也称单词字符。
W是[^0-9a-zA-Z_]。非单词字符。
s是[ tvnrf]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。
记忆方式:s是space character的首字母。
S是[^ tvnrf]。 非空白符。
. 就是[^nru2028u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式:想想省略号...中的每个点,都可以理解成占位符,表示任何类似的东西。
如果要匹配任意字符如何办?可以使用[dD]、[wW]、[sS]和[^]中任何的一个。
量词也称重复。掌握{m,n}的准确含义后,只需要记住一些简写形式。
{m,} 表示至少出现m次。
{m} 等价于{m,m},表示出现m次。
? 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?
+ 等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。
* 等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。
d{3,6} 贪婪模式(贪婪匹配) 尽可能多的匹配
其中正则/d{2,5}/,表示数字连续出现2到5次。会匹配2位、3位、4位、5位连续数字。
贪婪模式会尽可能多的匹配。你能给我6个,我就要5个。你能给我3个,我就3要个。反正只要在能力范围内,越多越好。
d{3,6}? 非贪婪模式(惰性匹配);尽可能少的匹配
通过在量词后面加个问号就能实现惰性匹配,因此所有惰性匹配情形如下:
{m,n}?
{m,}?
??
+?
*?
对惰性匹配的记忆方式是:量词后面加个问号,问一问你知足了吗,你很贪婪吗?
一个模式可以实现横向和纵向模糊匹配。而多选分支可以支持多个子模式任选其一。
具体形式如下:(p1|p2|p3),其中p1、p2和p3是子模式,用|(管道符)分隔,表示其中任何之一。
例1:
var reg = /good|nice/g;
var str = 'good idea ,nice try'
console.log(str.match(reg)); //['good','nice']
例2:
var regex = /good|goodbye/g;
var string = "goodbye";
console.log(string.match(regex)); //good
例3:
var regex = /goodbye|good/g;
var string = "goodbye"; console.log(string.match(regex)); //goodbye
以上结果说明,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。
正则表达式位置匹配攻略【转】
正则表达式是匹配模式,要么匹配字符,要么匹配位置。请记住这句话。
然而大部分人学习正则时,对于匹配位置的重视程度没有那么高。
本文讲讲正则匹配位置的总总。
内容包括:
1. 什么是位置?
2. 如何匹配位置?
3. 位置的特性
4. 几个应用实例分析
位置是相邻字符之间的位置。比如,下图中箭头所指的地方:
在ES5中,共有6个锚字符:
2.1 ^和$
^(脱字符)匹配开头,在多行匹配中匹配行开头。
$(美元符号)匹配结尾,在多行匹配中匹配行结尾。
比如我们把字符串的开头和结尾用"#"替换(位置可以替换成字符的!):
多行匹配模式时,二者是行的概念,这个需要我们的注意:
2.2 b和B
b是单词边界,具体就是w和W之间的位置,也包括w和^之间的位置,也包括w和$之间的位置。
比如一个文件名是"[JS] Lesson_01.mp4"中的b,如下:
为什么是这样呢?这需要仔细看看。
首先,我们知道,w是字符组[0-9a-zA-Z_]的简写形式,即w是字母数字或者下划线的中任何一个字符。而W是排除字符组[^0-9a-zA-Z_]的简写形式,即W是w以外的任何一个字符。
此时我们可以看看"[#JS#] #Lesson_01#.#mp4#"中的每一个"#",是如何来的。
第一个"#",两边是"["与"J",是W和w之间的位置。
第二个"#",两边是"S"与"]",也就是w和W之间的位置。
第三个"#",两边是空格与"L",也就是W和w之间的位置。
第四个"#",两边是"1"与".",也就是w和W之间的位置。
第五个"#",两边是"."与"m",也就是W和w之间的位置。
第六个"#",其对应的位置是结尾,但其前面的字符"4"是w,即w和$之间的位置。
知道了b的概念后,那么B也就相对好理解了。
B就是b的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉b,剩下的都是B的。
具体说来就是w与w、W与W、^与W,W与$之间的位置。
比如上面的例子,把所有B替换成"#":
2.3 (?=p)和(?!p)
(?=p),其中p是一个子模式,即p前面的位置。
比如(?=l),表示'l'字符前面的位置,例如:
而(?!p)就是(?=p)的反面意思,比如:
二者的学名分别是positive lookahead和negative lookahead。
中文翻译分别是正向先行断言和负向先行断言。
ES6中,还支持positive lookbehind和negative lookbehind。
具体是(?=p)和(?!p)。
也有书上把这四个东西,翻译成环视,即看看左边或看看右边。
但一般书上,没有很好强调这四者是个位置。
比如(?=p),一般都理解成:要求接下来的字符与p匹配,但不能包括p的那些字符。
而在本人看来(?=p)就与^一样好理解,就是p前面的那个位置。
对于位置的理解,我们可以理解成空字符""。
比如"hello"字符串等价于如下的形式:
也等价于:
因此,把/ hello$/写成/ ^hello$$$/,是没有任何问题的:
甚至可以写成更复杂的:
也就是说字符之间的位置,可以写成多个。
把位置理解空字符,是对位置非常有效的理解方式。
4.1 不匹配任何东西的正则
让你写个正则不匹配任何东西
easy,/.^/
因为此正则要求只有一个字符,但该字符后面是开头。
4.2 数字的千位分隔符表示法
比如把"12345678",变成"12,345,678"。
可见是需要把相应的位置替换成","。
思路是啥呢?
4.2.1 弄出最后一个逗号
使用(?=d{3}$)就可以做到:
4.2.2 弄出所有的逗号
因为逗号出现的位置,要求后面3个数字一组,也就是d{3}至少出现一次。
此时可以使用量词+:
4.2.3 匹配其余案例
写完正则后,要多验证几个案例,此时我们会发现问题:
因为上面的正则,仅仅表示把从结尾向前数,一但是3的倍数,就把其前面的位置替换成逗号。因此才会出现这个问题。
如何解决呢?我们要求匹配的到这个位置不能是开头。
我们知道匹配开头可以使用^,但要求这个位置不是开头如何办?
easy,(?!^),你想到了吗?测试如下:
4.2.4 支持其他形式
如果要把"12345678 123456789"替换成"12,345,678 123,456,789"。
此时我们需要修改正则,把里面的开头^和结尾$,替换成b:
其中(?!b)如何理解呢?
要求当前是一个位置,但不是b前面的位置,其实(?!b)说的就是B。
因此最终正则变成了:/B(?=(d{3})+b)/g
4.3 验证密码问题
密码长度6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符。
此题,如果写成多个正则来判断,比较容易。但要写成一个正则就比较困难。
那么,我们就来挑战一下。看看我们对位置的理解是否深刻。
4.3.1 简化
不考虑“但必须至少包括2种字符”这一条件。我们可以容易写出:
4.3.2 判断是否包含有某一种字符
假设,要求的必须包含数字,如何办?此时我们可以使用(?=.*[0-9])来做。
因此正则变成:
4.3.3 同时包含具体两种字符
比如同时包含数字和小写字母,可以用(?=. [0-9])(?=. [a-z])来做。
因此正则变成:
4.3.4 解答
我们可以把原题变成下列几种情况之一:
1.同时包含数字和小写字母
2.同时包含数字和大写字母
3.同时包含小写字母和大写字母
4.同时包含数字、小写字母和大写字母
以上的4种情况是或的关系(实际上,可以不用第4条)。
最终答案是:
4.3.5 解惑
上面的正则看起来比较复杂,只要理解了第二步,其余就全部理解了。
/(?=.*[0-9])^[0-9A-Za-z]{6,12}$/
对于这个正则,我们只需要弄明白(?=.*[0-9])^即可。
分开来看就是(?=.*[0-9])和^。
表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。
(?=. [0-9])表示该位置后面的字符匹配. [0-9],即,有任何多个任意字符,后面再跟个数字。
翻译成大白话,就是接下来的字符,必须包含个数字。
4.3.6 另外一种解法
“至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。
那么要求“不能全部都是数字”,如何做呢?(?!p)出马!
对应的正则是:
三种“都不能”呢?
最终答案是:
位置匹配相关的案例,挺多的,不一而足。
感谢你看到这里,本文也要结束了。
如果有更好的例子,也可以帮我补充补充。
最后,我们该想到,陆游诗人对前端做的最大贡献是:
纸上得来终觉浅,绝知此事要躬行。
本文完。
系列总目录传送门