整理正则表达式篇
-wuian7yulian
基础知识介绍 :
1> 字符串的组成:
对于字符串"ABC"来说,其包括三个字符 四个位置
可以理解为 三个人已经排好队 这样 再来一个人排队就有四个位置可以选择
2> 什么是占有字符:
正则表达式匹配过程中,如果子表达式匹配到东西,而并非是一个位置,并最终保存匹配结果当中,就成为占有字符
3> 什么是零宽度:
正则表达式在匹配过程中,如果子表达式匹配一个位置,或者匹配的内容并不保存到匹配结果当中,这种就称作零宽度
4> 占有字符和零宽度的区别:
占有字符是互斥的,而零宽度是非互斥的,即: 一个字符同一时间只能由一个子表达式进行匹配,但是一个位置 就可以由多个零宽度的子表达式匹配
5> 匹配控制权:
正则表达式由左到右依次进行匹配,通常情况下是有一个表达式取得控制权的
6> 匹配规则:
(正确的理解零宽度)
以"ABC"为例子 :
分析:"ABC"有三个字符但是有四个位置 比如说A前面的位置是 location1, A和B中间是location2 ..C后面就是location4
那么 分析一个正则 reg="AC"; 它去和字符串匹配
过程为:
首先是reg的子表达式 即 : "A"
现在是"A"(正则表达式的)[后 称为 A(正)] 取得了控制权 从字符串(从左向右)的 位置 location1 进入字符串,向右遇到一个A字符,匹配成功,于是就把源数据中的A存放到结果当中(占位字符),表明它占有了一个字符,而接下来 就将控制权转交给了C(正),注意!!! 现在C(正)的进入位置就成了location2!!从这开始发现后面字符是B,匹配失败
再分析一个零占位的reg reg="^A";
^进入字符串位置 location1 匹配成功 A(正)获得控制权 进入字符串位置 仍然为location1 这就是零占位大概的原理
他们两个的区别就为: 一个是越过了字符(或者认出字符) 一个根本不是匹配字符(而是匹配他们中间的位置)
7> 元字符:
-----元字符--||----------描述-------------------------
. || 匹配 除换行符以外的任意字符
\w || 匹配 字母数字或下划线或汉字
\s || 匹配 任意空白符
\d || 匹配 数字
\b || 匹配 单词开始或者结束,(匹配的是位置)
^ || 匹配 字符串开始
$ || 匹配 字符串结束
------------------------------------------------------
8> 转义字符:
如果想要得到元字符本身的话 需要使用"\" 来取消这些元字符的特殊意义
"wahsd\waskdhk" 如果我想匹配这个字符串中的\wa
那么:
在js(不需要进行编译的语言)中 正则可以写成:"\\wa"
在java中(需要先由编译器解析的语言)中 需要写成"\\\\wa"
原因:正常情况下 编译语言 需要先解析一次这个字符串 而java中的\也是转译符号
我们要用一个\的话 就需要写\\ 这样才识别
即上面那句"\\\\wa" 经过编译器后就成为了"\\wa" 所以最终的正则表达式识别的就是这个
9> 字符类:
字符类就是指使用"[]"括起来的 是严格区分大小写的
需要注意的是仅仅是匹配一个! 唯一匹配一个
例1: [osP] 就是匹配其中的一个o或者s或者P
例2: [a-zA-z0-9]就是匹配一个任意大小写字符或者数字
10> 限定符:
----限定符----||----------Description-------------------------
前面紧挨着的正则匹配
* || 重复 0次 或 多次
+ || 重复 1次 或 多次
? || 重复 0次 或 1次
{n} || 重复 n次
{n,} || 重复 n次 到 +∞
{n,m} || 重复 n次 到 m次 -闭区间
------------------------------------------------------
11> 分支条件:
用"|"把不同的规则进行分割
或者的关系 | 前后都是两个完整的表达式
注意点: 分枝条件的顺序 正则表达式从左向右依次匹配 若满足了某个分支 就不会再管其他分支了
12> 分组:
可以使用()小括号来指定表达式 小括号里面的代表一组
例: (\d{1,3}){3} 就是\d{1,3}重复了三次
13> 翻译字符:
----表达式----||----------Description-------------------------
\W || 匹配任意一不是字母数字下划线的字符
\S || 匹配任意一个不是空白字符的字符
\D || 匹配不是数字的字符
\B || 匹配不是单词开头或者结尾的位置
[^X] || 匹配除了X意外的任意字符
[^a-z] || 匹配除了小写字母意外的任意字符
[^aeixy] || 匹配除了aeixy意外的任意字符
------------------------------------------------------
14> 后向引用:
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是分组捕获的内容)做进一步处理,默认情况下 每个放入怒都会自动拥有一个组号
规则是:从左向右,以分组的左括号为标志第一个出现的分组的组号为1 第二个为2
其他规则:
1) 分组号为0的是对应整个正则表达式
2) 组号分配过程是要对正则表达式扫描两遍的:第一遍只给未命名组进行分配组号,第二遍只给已经命名的组分配
所以所有命名组的组号都大于未命名的组号
3) 也可以使用(?:exp)这样的方式来限制一个分组对组号的分配的参与权
--
后向引用的概念 后向引用用于重复搜说前面某个分组匹配的文本 \1代表分组1匹配的文本
例: 正则表达式为(a)(b)(c)\2 匹配 abcb 成功
他们的分组号分别为 1 2 3 \2代表第二组匹配到的文本
取消组号例: reg=(?<g1>abc)(?<g2>cde)(?:\d{3})
原文本: test=abccde298
原理: 首先扫描reg的组 以左括号为基准一共有三个组 而且前两个组已经分配了组名,只有最后一个没有分配组名,即为未命名的组 规则是献给未命名的组进行分配组号但是改组用了(?:exp)来避免了组号分配所以说他没有组号 然后给命名的组进行分配组号 分表为$1 $2 且整个正则的组号为$0
15> 零宽断言和负零宽断言:
----------------------------------------------------
||(exp) ||匹配exp并捕获文本到自动命名的组里
捕获 ||(?<name>exp) ||匹配exp并捕获文本到名称为name的组里
||(?:exp) ||匹配exp不捕获匹配文本也不给分组分配组号
----------------------------------------------------
||(?=exp) ||匹配exp前面位置但是不匹配exp
断言 ||(?<=exp) ||匹配exp后面位置但是不匹配exp
||(?!exp) ||匹配后面不是exp的位置但是不匹配exp
||(?<!exp) ||匹配前面不是exp的位置但是不匹配exp
----------------------------------------------------
注释 ||(?#comment) ||注释
----------------------------------------------------
16> 零宽度断言:
1. (?=exp) :
也叫零宽度正预测先行断言,她匹配自身出现的位置后面能匹配表达式exp
注意预先这个词组 比如 我们匹配"asdhaking"
正则为\w+(?=king) 匹配结果为asdha
原理为: 第一步到 a字符前面的位置\w匹配上他会看一下后面是king吗 只是看一下 并不做结果捕捉获取 然后继续向后 依次类推到a的时候看到后面是king 则匹配结束 返回前面匹配内容
2. (?<=exp) :
又叫零宽度正回顾断言,它匹配自身出现位置的前面匹配表达式exp
例 reg = (?<=ing)\d{3} test="123inging123123"
结果为中间的123
17> 负向零宽断言:
1. (?!exp) :
零宽度负预测先行断言
断言此位置后面跟的不能匹配表达式exp
2. (?<!exp) :
零宽度负回顾断言
断言此位置前面跟的不是exp的位置
18> 平衡组:
----------------------------------------------------
||(?'group') || 把捕获的内容密码为group,并压入堆栈
||(?'-group') || 从堆栈上弹出最后压入堆栈名为group的捕获内容
||如果堆栈为空在本组匹配失败
||(?'group'yes|no) || 如果堆栈上存在名为group的捕获内容的话,
||继续匹配yes部分的表达式,否则匹配no表达式
||(?!) || 零宽度负先行断言 由于没有后缀表达式,试图匹配总是失败
----------------------------------------------------
压栈: (?'t1'123)(?'t1'456) 对应123456
group组(栈(先进后出)) 显示的数据就是456
弹栈: (?'t1'123)(?'t1'456)(?'-t1') 对应123456
group组 压了两次栈 第一次压入文本123 第二次压入456 然后弹出一次数据 则显示123
(?(group)yes|no)
栈的使用中我们无法预测栈是否已经到栈底 这个正则解决了这个问题 意义为 若 栈空 -> no 表达式
若 未空 -> yes 表达式
例 (?'t1'123)(?'t1'456)(?'-t1')(?(t1)1|2)
匹配 12345611234562
匹配到的是1234561
而(?'t1'123)(?'t1'456)(?'-t1')(?'-t1')(?(t1)1|2)
匹配到的是1234562
19> 贪婪与非贪婪:
以上例子实际上我们都是用的贪婪模式
例: (123).+25 对用匹配1235252525
匹配结果为 1235252525 全部匹配到 而实际上我们常认为应该是123525
首先 贪婪和非贪婪是对!!子表达式!!的匹配行为指定添加的概念!!!
贪婪模式: 在整个表达式匹配成功的前提下尽可能多的匹配 (子表达式匹配内容尽可能靠后)
非贪婪模式: ...尽可能少的匹配
非贪婪模式限定符 带问号?的限定符
-------------------------------------------------------------
*? | 匹配上一个元素 零次或多次,但次数尽可能少
+? | 匹配上一个元素 一次货多次,但次数尽可能少
?? | 匹配上一个元素 零次或者一次,但..
{n}? | 匹配前导元素 恰好n次
{n,}? | 匹配上一个元素 至少n次,但次数..
{n,m}? | 匹配上一个元素次数介于n到m次之间,但次数..
--------------------------------------------------------------
20> 贪婪匹配和非贪婪(懒惰)匹配原理
非贪婪模式只被部分NFA引擎所支持 控制权的问题
未完全深入理解 待后期研究
易错笔记本:
- 21> 正则表达式出错引发注意点 有在使用中犯错的地方 感觉需要注意的点 我会在这更新
1 正则取组名必须要使用单引号
2 匹配目标是\的话 是必须需要转译的
3 分组中每个都是单个 组中 [1sa] 就是1或者s或者a 但是[a\d]就不再是了 是代表a或者数字
4 如要匹配括号 纯字符硬匹配的话 必须需要写成\( 中括号也一样 因为正则的字符用到了 相当于关键字 有意义
而实际上在分组中[a(s] 是能匹配到(的 相当于a或者\或者s 但是建议用转译字符转译一下 以防与外部正则表达式错误配对
--4.18日更
参考文件:
http://www.cnblogs.com/deerchao/archive/2006/08/24/zhengzhe30fengzhongjiaocheng.html#mission