正则表达式基础
很久之前便接触过正则,也用过不少,但是也从来没自己系统性总结下,反而每次都是去搜索别人的博客,突发奇想问了下CTF队伍中同学是否常使用正则表达式,发现还是挺重要的,也想对这块进行一些系统性的总结,涉及的内容可能会比较浅,会尽量通俗易懂,结合例子让读者更能理解。
什么叫做正则表达式?
在进行编程开发时,经常需要进行字符串的操作,如查找、替换等,其中很重要的一点便是匹配符合某种规则的字符串,而正则表达式就是对这些规则的描述,可以使用正则表达式对字符串的格式、规则或含有哪些字符进行描述。正如在linux系统中,如果需要搜索test目录下的python脚本文件,则需要使用的,命令为 "find ./test -name ‘*.py’ " ,其中 “*.py” 中的 ‘*’ 即为匹配任意字符串,这里其实和正则表达式也类似,叫做通配符。而正则表达式则是比这样的规则更为复杂的,能够准确描述一些特殊字符串格式的规则字符串。
入门与简单示例
先介绍一些简单的例子,从而对正则表达式有一个基本的理解。上文已说过,正则表达式其实是一种对字符串格式进行描述的字符串,所以在这样的字符串中,需要规定某些特殊字符、字符组合、符号用以表示一些特殊的含义,比如上述所说的 “*.py” 中的 “*” 就是这样一种,所以,在开始举例前,先介绍一些比较常用的特殊代码(或者叫元字符)
\b 匹配字符(串)的开头和结尾,用以规定需要匹配的字符串和其余字符串的分界处 |
上述字符中,后三个或许应该称为限定符,下面使用这些基本的元字符构造几个正则表达式的示例:
- \ba\w*\b
匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。 - \b.*xiaoZ.*\b
匹配一串字符,字符前是任意数量的除换行符外的任意字符,然后是 “xiaoZ” 最后是任意数量的除换行符外的任意字符。’.*’ 表示匹配任意数量的除换行符外的任意字符。 - \d+
匹配一个或更多连续的数字。
这里的例子比较简单,但是可能也有人会想到,如果需要匹配字符为 ‘\b’ 呢?如果直接用在正则中进行匹配的话则会将其解释为匹配字符串开头和结尾,所以这里需要进行转义,转义在编程中用的比较多,一般使用反斜杠来取消这些字符的特殊意义,正则表达式中也是一样,只需要使用 ‘\’ 取消字符中 ‘\’ 的特殊含义,即可实现该效果,即使用 ‘\\b’ 。总结一下就是,如果我们需要匹配的字符中含有作为正则表达式规定的元字符时,需要使用反斜杠符号进行转义操作。说到这个,你觉得我在写这个博客的时候,要出现 ‘\\\\’ 应该写几个反斜杠呢…
正则表达式进阶
重复与分组
可以看到,上述匹配中除了使用单一的匹配完,还使用了 ‘*’ 这样的元字符匹配多个数量的字符,在正则表达式中,使用来表示重复的元字符也叫做限定符,主要有以下几个:
* 与前面使用的元字符连接,表示前一个使用的元字符表示的字符匹配任意次(零次或多次) |
比如,表示匹配xiaoZ后跟1个或者更多数字使用下列正则表达式:
xiaoZ\d+ |
上述所使用的重复限定符中,只是对单个字符进行重复,如果需要对于多个字符进行重复呢?
这个时候需要使用的就是分组了,如果在匹配一个字符串时,需要匹配的是满足某串字符重复多次的字符串时该如何匹配?这里使用的就是分组,其实叫做分组可能并不是很准确,倒不如理解为分组重复,即对于某种特定的规则,需要重复匹配多次。
分组中使用的是小括号结合上述所说的限定符,如果需要表示 ‘xiaoZ’ 重复3次则使用:
(xiaoZ){3} |
如果需要匹配IP地址,则使用下列正则表达式:
(\d{1,3}\.){3}\d{1,3} |
很显然,上述正则表达式只是简单的匹配,而对于IP地址来说,对于每一位的最大值都是有固定规定的,所以需要进行一些比较的操作,可以使用下列正则表达式(其中可能使用了后面才讲到的字符,可以先跳过,随后返回进行分析):
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) |
后向引用
那么这里又存在另外一个问题了,如果我现在在一个字符串中,某串字符可能重复多次,但是在不同的地方又该如何表示?此时简单的使用重复与分组已经用处不大,所以需要使用另外一种表示方式,即后向应用。
在正则表达式中,后向引用用于重复搜索前面某个分组匹配的文本。其用法为:使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
举个例子进行说明
\b(\w+)\b\s+\1\b |
可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)
当然,对于组名的指定,也可以自己进行指定,比如上述匹配单词,则指定一个子表达式的组名为Word,需要使用这样的语法:(?
\b(?<Word>\w+)\b\s+\k<Word>\b |
另外,使用小括号的时候,还有很多特定用途的语法。如下:
捕获 |
预定义字符组
上述正则表达式中,所用到的匹配都是对于某一类字符进行匹配,那么如果需要编写一个程序,查找一串字符串中包含有 ‘abc’ 三个字母的个数有多少个需要如何表示呢?在正则表达式中,可以通过将字符预定义为类来匹配指定的字符,格式为 ‘[需要匹配的字符集]’ ,如上述例子则使用 ‘[abc]’ 匹配三个字母中任意一个,而这样的字符集结合限定符进行使用,则能够更好的匹配一些复杂的字符串。
除了直接指定字符外,也可以指定字符的范围,如使用 ‘[0-9]’ 匹配0到9的数字,’[a-z0-9A-Z]’ 则与 ‘\w’ 具有同样含义。(注意这里使用的不同范围直接写在一起)。
分枝条件
先看一个例子:
\(?0\d{2}[) -]?\d{8} |
上述对于电话号码进行匹配的正则表达式,看似挺不错,但是观察可以看到,这里因为 ‘(’ 和 ‘)’ 中使用的是 ‘?’ 匹配每次使用0次或1次,则如果现在给出的是 ‘010)12345678’ ,虽然给出的电话号码是错误的,但是依旧是符合这个正则表达式的。如何解决这个问题呢?
对上述问题进行分析,出现这样问题的原因在于对括号或一些可有可无的字符的匹配存在问题,类似于一个排列问题,符合上述正则表达式的排列有8种(’(?’ 为2,’[) -]?'为4,相乘为8),但是其实我们需要匹配的电话号码只是这8种中的一部分,所以可以我们需要的那几种排列列出,满足这几种排列任意一种的正则表达式的字符串即可匹配。而在将这几种进行排列时,使用的就是分枝条件,这里使用的符号为 ‘|’ ,如下列正则表达式:
0\d{2}-\d{8}|0\d{3}-\d{7} |
这一正则表达式则是将两种排列分别列出,使用’|’ 符号进行连接,用以匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。
再比如:
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8} |
这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。
这里需要注意的是,在使用分枝条件时,需要使用各个条件(排列)的使用顺序,在使用含有多个条件的正则表达式进行匹配时,只会对第一个满足了的条件进行匹配。如上述使用的匹配电话号码的分枝条条件,满足前一条件时,则不会再去匹配其余条件。
反义
上述对各种不同的元字符进行了介绍,那么正如数学中所学的补集一般,有时需要使用正则表达式来匹配除了某种或某个字符外的所有字符,则需要使用反义字符来表示,常用的反义字符如下:
\W 匹配任意不是字母,数字,下划线,汉字的字符 |
比如: ‘\S+’ 匹配不包含空白符的字符串
结语
对正则表达式的简单介绍也就到这里了,也就仅限于简单介绍了,认真看完的话,应该已经能够看懂一些正则表达式,也能自己写一些了,但是毕竟写的不是很深入,也可能存在一些问题,请包涵,另外的话,如果有想自己深入去看一些关于正则表达式的东西的话,推荐看看关于正则表达式语言元素的MSDN在线文档