2.1 一般形式
- 上一节我们了解到字符组 [0-9] 或 \d 可以匹配单个数字字符。现在我们使用正则来验证更复杂的字符串,比如国内邮政编码。很自然,\d 匹配单个字符,那么我们可以这样写:
re.search(r"^\d\d\d\d\d\d$", "110016") !=None # => True
-
这样写的话,太麻烦了, \d 重复了6次,不够灵活方便。为此,正则表达式提供了量词,上面的匹配格式可以简写为 \d{6}。意思就是 匹配 6个数字。
下面举几个简单的例子:
re.search(r"^\d{6}$", "100861") !=None # => True
re.search(r"^\d{6}$", "1A0861") !=None # => False
-
量词还可以不确定长度,通用形式是 {m,n},注意中间逗号后面不跟空格。它限定之前的元素能够出现的次数,m是下线,n是上限。均为闭区间。\d{4,6},表示这个数字字符的长度最短是4个字符,最长是6个字符。
量词的限定出现次数一般都有明确下限,如果没用,则默认为0。有些语言可以省略0,写作 {,n},但是这样不是通用的,建议写作 {0,n}。
下表展示了量词的一般书写形式:
-
{n}之前的元素必须出现n次。
-
{m,n}之前的元素最少出现m次,最多出现n次。
-
{m,}之前的元素最少出现m次,出现次数无上限。(注意:并非真正意义上的无上限,隐式上限是65536 )
-
{0,n}之前的元素可以不出现,也可以出现,最多出现n次。
2.2 常用量词
-
{m,n} 是通用形式上的量词,正则表达式还有3个常用量词,分别是 +、?、*,它们的形态虽不同于 {m,n},但是功能也相同,可以看作是量词的简记法。
-
常用量词书写形式:
-
* 等价于 {0,} 意为可能出现,也可能不出现,出现次数无上限。
-
+ 等价于 {1,} 意为至少出现1次,出现次数无上限。
-
* 等价于 {0,1} 意为至多出现1次,也可能不出现。
大部分情况下只需要表示上面这三种意思,所以常用量词的使用频率高于 {m,n},例子如下:匹配 traveler 和 traveller (美式和英式)
re.search(r"^traverll?er", "traveler") !=None # =>True
re.search(r"^traverll?er", "traveler") !=None # =>True
量词也广泛用于解析HTML代码,匹配各种标签及标签中的内容。例如匹配所有 html tag的正则表达式:<[^>]+>;匹配 open tag , <[/][>]*>;匹配 close tag ,</[^>]+>;匹配 self-closing tag,<[^>/]+/>
2.3 数据提取
- 之前使用的python正则查询函数都是 search(),如果匹配成功,返回一个MatchObject的对象,这个对象包含了匹配的信息,比如匹配的结果。可以使用
re.search(r"^traverll?er", "traveler").group(0) 来获取匹配结果。
- 这里再介绍一个python的方法,findall(pattern,string),如果匹配成功,返回的是一个数组。例如:使用findall()来匹配两个邮政编码。注意:search 和 findall 都是python正则表达式库的函数,其他语言不一样。
re.findall(r"\d{6}", "zipcode1:123456, zipcode2:987654")
得到的结果就是 ['123456', '987654'],注意这里我们需要匹配6个数字,所以不需要在正则表达式外面加上 开始 和 $结尾来限定。
2.4 点号
-
上一章提到过, "." 号可以匹配任意字符,但是换行符 \n 这种不能匹配。要匹配任意字符可以 [\w\W] 这种。因为点号几乎可以匹配任意字符,所以实际运用中很多人图省事,随意使用 .* 或者 .+ ,结果却事与愿违。下面我们看一个例子:
我们希望匹配的字符串内容是 "quoted string",待匹配的字符串也是 "quoted string" ,注意这里匹配包含前后双引号。我们这样写没问题:
re.search(r"\".*\"","\"quoted string\"").group(0) # => "quoted string"
- 这样得到的结果没问题,那么我们再变一下。现在我们改变一下待匹配字符串,"quoted string" and another",然后我们用同样的 pattern 去匹配这一段字符串,得到的结果却是 "quoted string" and another",这个结果不是我们想要的 "quoted string" 。
- 这里作简单解释。在正则表达式 "." 中,点号 "." 可以匹配任何字符, * 表示可以匹配的字符串长度没有限制 ,所以 . 在匹配过程结束以前,每遇到一个字符(除去无法匹配的\n), .*都可以匹配,但是到底匹配这个字符,还是忽略它吗,交给之后的 " 来匹配呢?
- 答案是取决于所使用的量词。正则中的量词分为几类,之前介绍的量词都可以归为一类,叫做匹配优先量词(也有译为贪婪量词)。意思就是在拿不准是否要匹配的时候,优先尝试匹配,并且几下这个状态,以备“反悔”。
来分析正则表达式 ".*" 对字符串 "quoted string的匹配过程 - 开始," 匹配 " ,然后轮到 q , .* 可以匹配它,也可以不匹配,因为使用的量词都是匹配优先量词,所以 .先匹配q,并且记录下这个状态【q也可能是.不该匹配的】;
- 接下来是字符 u, .* 可与匹配它,也可与不匹配,因为使用的量词都是匹配优先量词,所以 .先匹配g,滨记录下这个状态【g也可能是.不该匹配的】;
- …………
- 最后是末尾的 " , .* 可与匹配它,也可与不匹配,因为使用的量词都是匹配优先量词,所以 .先匹配" ,滨记录下这个状态【" 也可能是.不该匹配的】;
这个时候,字符串后已经没有字符了,但正则表达式中还有 " 没有匹配,所以只能查询之前保存的备用状态,看看能不能退回几步,按照 " 的匹配。查询到最近状态是: 【" 也可能是.不该匹配的】。于是让 . 反悔 对 " 的匹配,把字符串 " 交给 正则里面的 " 来匹配,测试发现正好匹配,所以整个匹配宣告成功。这个反悔的过程,专业术语叫做回溯(backtracking)。
image.png
至于 "quoted string" and another" 也是一样的道理,最开始 .* 匹配了 字符串 * 后的所有内容,后来发现正则中还有 " 未匹配,因此回溯最近的 一个 " ,交给匹配字符李的 " 进行匹配。
![](https://img.haomeiwen.com/i19069836/32ad632d2bedfa02.png)
- 因此要准确匹配 "quoted string" and another" 字符串中的 "quoted string" ,就不能图省事使用 "." 来匹配,需要使用 "[^"]"
image.png
2.5 忽略优先量词
- 但某些时候,也会需要使用到 .* 匹配,比如匹配 <script type="text/javascript">...</script> ,script标签左右的开关标签好写, 但是js内部的代码可以出现任意字符,所以这个时候还是得 用 .* 来匹配。.* 不能匹配换行符,所以这里用 [\s\S]* 来匹配。
str1 = """
<script type="text/javascript">
alert("1");
</script>
<br />
<script type="text/javascript">
alert("2");
</script>
"""
print(re.search(r"<script type=\"text/javascript\">[\s\S]*</script>",str1).group(0))
输出
![](https://img.haomeiwen.com/i19069836/7ce778a47eba51e5.png)
- 表达式是匹配成功了,但是最后的结果不正确。 上述匹配规则会一次性匹配2段JS代码,包括中间的换行符 <BR />。
换个角度,通过改变 [\s\S]* 的匹配策略来解决问题:在不确定是否需要匹配的场合,先尝试不匹配的选择,测试正则表达式中后面的元素,如果失败,再退回来尝试 .* 匹配,如此就没有问题了。 - 这就是 忽略优先量词模式(懒惰量词 Lazy Quantifier)。如果不确定是否需要匹配,懒惰量词会选择“不匹配“状态,再尝试表达式中之后的元素,如果尝试失败,再回溯,选择之前保存的匹配状态。
re.search(r"<script type=\"text/javascript\">[\s\S]*?</script>",str1)
再 * 后面增加了 一个 ? 号。这样就能提取出所有 js标签及代码。
匹配优先量词与忽略优先量词逐一对应。所有的匹配优先量词后加 ? 即可。
例如 * 对应 *? 。
忽略优先量词可以提取 c 语言中的护士、提取网页中超链接tag、解析表格等。
- 使用正则表达式去拆解 linux 路径
print(re.search(r"^.*/"), "/usr/local/bin/pyhton")
- 拆解windows路径
print(re.search(r"^.*\"),"c:\program files\")
2.6 转义
- 前面我们知道了,*、+、? 等作为量词的字符具有特殊意义,但某些情况下我们就要匹配它们本身,则再他们前面加 \ ,例如 +。
- 如果是 {m,n},则 {m,n}
- ?? , ??
网友评论