正则表达式小结
正则表达式小结
此篇系读《正则表达式必知必会》后个人对正则的理解和学x总结,正则表达式在waf中或者siem日志分析等项目中扮演了非常重要的角色,个人认为是研究bypass和日志分析的基础,以此为基础后面可能还会写写Mod Security CRS的解析。
简介
正则表达式(regular expression, regex)是一种工具,简而言之就是匹配字符串的一串符号,常用来模糊查找,校验输入的合法性等。
单个字符匹配
- 纯文本如
my
其实本质上也是正则表达式
这样匹配出来有两个结果,但大多数正则默认是只返回第一个匹配,想要全局匹配一般都是返回数组或者其他专用格式。
- 正则区分大小写,比如Ben不匹配ben, 忽略大小写匹配一般可以用i参数。
.
可以匹配任意字符(换行符除外的字母、字符、数字),如c.t 可以匹配cat和cot- 匹配正则表达式中有特殊含义的符号需要用到转义字符,如匹配
.
本身可以使用表达式\.
匹配一组字符
[]
里面的内容可以匹配其中一个字符,如[ns]
可以匹配n或者s[]
里面的内容可以匹配字符区间,如[0123456789]
和[0-9]
是等价的,[a-zA-Z0-9]
可以匹配任意字母和数字^
可以表示取非匹配,如[^0-9]
表示非数字的集合
元字符
- 元字符是正则中含有特殊含义的字符,比如
.
表示匹配任意字符,[
表示字符集合的开始,匹配元字符时就需要用到转义字符,如匹配数据表达式array[1]的正则表达式要写作array\[1\]
- 匹配空白字符,
\s
可以匹配任何一个空白字符(等价[\f\n\r\t\v]
),\S
可以匹配任何一个非空白字符(等价[^\f\n\r\t\v]
)
- 元字符匹配数字,
\d
可以匹配任意一个数字字符,\D
可以匹配任何一个非数字字符 - 元字符匹配字母和数字,,
\w
可以匹配任意一个不分大小写的字母数字字符或下划线,\W
可以匹配任意一个非不分大小写的字母数字字符或下划线 - 元字符匹配十六进制和八进制数值,如、0x0A对应与ASCII字符10(换行符号),效果等价于
\n
, 八进制使用前缀\0
- POSIX字符类是许多(但不是所有,如JS不支持)正则表达式都支持的简写形式
重复匹配
- 匹配一个或者多个字符的重复加上
+
字符就可以了,+
匹配一个或者多个字符(至少一个),如[0-9]+
匹配一个或多个连续的数字 - 匹配零个或多个字符,可以使用
*
, 如\w+[\w]*
可以匹配大小写字母数字或者下划线开头的字符串 - 匹配零个或一个字符,可以使用
?
,?
之作用于其前一个字符或者集合。如要匹配URL, 可能有http的,也有https的。完全匹配这样的开头就可以使用https?
- 匹配确定的重复次数可以使用
{次数}
,如匹配长度为4的16进制数表达式[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]
非常长,可以写为[0-9A-Fa-f]{4}
- 匹配次数可以设置区间,如
[0-9A-Fa-f]{0,6}
表示0-6位的16进制数 - 匹配“至少重复多少次”,如找出位数大于3的数字可以用
\d{3,}
表示 - 防止过度匹配(贪婪表达式和懒惰表达式)
如有一段字符串
<p>Hello</p> and <p>World!</p>
使用正则表达式<p>.*</p>
中的.*
其实匹配的是Hello</p> and <p>World!
。因为*和+都是所谓的“贪婪型”元字符,这些元字符匹配模式是多多益善而不是适可而止的。如果不想用这些原字符的贪婪模式怎么办?可以在其后面加上?
来实现最小匹配,对应关系如下:
贪婪型元字符 | 懒惰型元字符 |
---|---|
? | |
+ | + |
{n,} | {n,}? |
位置匹配
位置匹配是解决在什么位置进行字符串匹配的问题
- 单词边界空格符号
\b
, 如\bcat\b
在句子“The cat scattered his food all over the room”中匹配cat但是不匹配scattered;如\B-\B
匹配pass-key但不匹配两头为空的-
符号 - 字符串边界,
^
用在[]
里面表示非,也可以匹配字符串开头,如^\s*<h1>
可以匹配<h1>
开头的字符串也能对合法的空白字符做妥善处理。同理的$
可以用来匹配字符串的结尾,如</[Hh][Tt][Mm][Ll]>\s*$
可以匹配html文件的结尾。 - 分行匹配模式,部分正则表达式是支持分行模式的
?m
,不像上一条介绍的只能匹配开头的字符串,表达式(?m)^\s*//.*$
是可以匹配代码中//注释的行的,它将把换行符视为一个字符串分隔符
使用子表达式
`(non-breaking space)是html语言中的非换行空格字符,通常是用在
Windows 10这类短语中间,保证短语显示在同一行上,如果要匹配这些 ,一般会想到用
{2,},但其实这个表达式匹配的是形如
;;;;字符串,想要匹配多个
 ;需要用到子表达式
( ){2,}`|
表示或,如匹配年份的表达式中(19|20)\d{2}
就一定要将 子表达式扩起来明确运算符作用范围。- 子表达式的嵌套,搞懂了嵌套问题,理论上非常复杂的表达式也可以用正则匹配出来,搞懂子表达式嵌套的关键是一定要写清楚你想匹配什么和不想匹配什么。如匹配一个合法的IP地址应该是有以下规则
正则就可以写为:
(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))
回溯引用:前后一致匹配
- 回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式。可以理解成某个子表达式的在前面匹配中匹配出来变量作为后面匹配规则。回溯引用可以用
\数字
,如\1
表示模式的第一个子表达式,如\2
表示模式的第二个子表达式。如使用正则<h[12]>.*?</h[12]>
匹配<h1>fdsaf</h1>
也可以匹配<h1>fdfa</h2>
,想要表达式匹配前后一致仅匹配<h1>fdsaf</h1>
,使用回溯写正则可以写为为<h([12])>.*?</h\1>
- 回溯匹配也常用来做替换,JS中回溯使用符号
$
- 回溯可以用来做大小写替换,正则中有用来做大小写转换的元字符
前后查找
有些时候通过正则匹配的出的字符串只有一部分是我们想要的内容,但又不得不通过一段前后连接的特征来定位,如取html的title字段<title>hello</title>
, hello是我们想要的,但没有<title>
是没法定位匹配的。这里当然可以用前面的子表达式回溯的方式来取值,但同时取<title>
这标签即浪费时间也毫无意义。这时前后查找的作用就凸显出来了。
- 向前查找是以
?=
开头的子表达式,需要匹配的文本跟在=的后面。如取url里的协议名http://www.fucguigui.com
可以使用正则表达式.+(?=:)
- 向后查找的操作符是
?<=
(记为箭头指向文本阅读方向的后方),如要取$99.99
中的99.99而不想取到$符号,可以使用表达式(?<=\$)[\d.]+
- 向前向后查找结合,如
(?<=(<title>)).*(?=(</title>))
就匹配到<title>hello</title>
中的hello - 对前后查找取非,这个很少用到了
嵌入条件
嵌入条件只可能出现在两种情况:根据回溯引用进行条件处理或者根据前后查找来进行条件处理。
- 在条件里。回溯引用编号不需要被转义,如
(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)
, 这段正则的意思是匹配img标签,如果img标签在a标签里面则连开头的a标签一起匹配,?(1)
检查第一个回溯引用是否存在,条件满足才执行后面的表达式。 - 前后查找条件只在一个向前查找或向后茶轴操作取得成功的情况下才允许一个表达式被利用。如查找符合美国邮政编码编码规则的形如12345或12345-1234形式的字符可以用正则
\d{4}(?(?=-)-\d{4})
, 其中的(?=-)
匹配-但不消费-,如果五位数字匹配完成后剩下字符串向前匹配到- 则执行-\d{4}
。
还没有评论,来说两句吧...