在Python中使用正则表达式

概念

  正则表达式,又称规则表达式,简称为regex,是文本模式的表示方法。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。

  正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

  • 给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
  1. 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”);
  2. 可以通过正则表达式,从字符串中获取我们想要的特定部分。
  • 正则表达式的特点是:
  1. 灵活性、逻辑性和功能性非常强;
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。

用正则表达式查找文本模式

创建正则表达式对象

  Python中所有正则表达式的函数都在re模块中。在使用时,用import将其引入。向re.compile中传入一个字符串值,表示正则表达式,它将返回一个Regex模式对象。

  要创建一个Regex对象,就要输入以下代码:

1
2

NumRegex = re.compile(r'\d\d\d')

  在Python中,转义字符使用倒斜杠(\)。字符串’\n’表示一个换行字符,而不是倒斜杠加上一个小写的n。你需要输入转义字符\\,才能打印出一个倒斜杠。所以'\\n'表示一个倒斜杠加上一个小写的n。但是,通过在字符串的第一个引号之前加上r,可以将该字符串标记为原始字符串,它不包括转义字符。

  因为正则表达式常常使用倒斜杠,向re.compile()函数传入原始字符串相对来说就比输入额外的倒斜杠方便,即输入r'\d\d\d'比输入'\\d\\d\\d'要容易得多。

匹配Regex对象

    Regex对象的search()方法查找传入的字符串,寻找该正则表达式的所有匹配。如果字符串中没有找到该正则表达式,search()方法就会返回None。如果找到了该模式,search()方法将会返回一个Match对象。Match对象有一个group()方法,它返回被查找字符串中实际匹配的文本。下面是演示代码:

1
2
3
4
5
import re

phoneNumberRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
mo = phoneNumberRegex.search("My Number is 415-555-4242")
print('Phone number found: ' + mo.group())

  这样,将会打印匹配到的415-555-4242这个电话号码。

用正则表达式匹配更多模式

利用括号分组

  假定我想要上面那个电话号码的区号,即将区号从电话号码中分离,可以添加括号使正则表达式分组:'(\d\d\d)-(\d\d\d-\d\d\d\d)'。然后再使用group()方法匹配对象方法,从一个分组中获取匹配的文本。

  正则表达式字符串中的第一对括号表示第一组。第二对括号表示第二组。向group()方法中传入整数1或2,就可以匹配文本的不同部分。向group()方法传入0或不传入参数,将返回整个匹配文本。下面是演示代码:

1
2
3
4
5
6
7
import re

phoneNumberRegex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
mo = phoneNumberRegex.search("My Number is 415-555-4242")
print(mo.group(0))
print(mo.group(1))
print(mo.group(2))

这样将依次返回415-555-4242415555-4242这三个结果。如果想一次就获得所有的分组,可以使用groups()这个方法,这样将会返回多个值得元组。

  但是,括号在正则表达式中有特殊的含义,如果你要在文本中匹配括号的话,请在括号对前输入倒斜杠。即传递给re.compile()的原始字符串中,\(\)转义字符将会匹配实际的括号字符。

用管道匹配多个分组

  字符’|’称为管道。希望匹配许多表达式中的一个时,就可以使用它。例如正则表达式r'Batman|Tina'将匹配Batman或Tina。

  也可以使用管道来匹配多个模式的一个,作为正则表达式的一部分。例如,假设你希望匹配'Batman''Batmobile''Batcopter''Batbar'中任意一个,由于所有的这些字符串都以Bat开始,所以如果能够只指定一次前缀,就很方便。可以通过括号实现。下面是演示代码:

1
2
3
4
5
6
import re

batRegex = re.compile(r'Bat(man|mobile|copter|bat)')
mo2 = batRegex.search('Batmobile lost a wheel')
print(mo2.group(0))
print(mo2.group(1))

这样将会打印Batmobilemobile。方法group()返回了完全匹配的文本’Batmobile’,而group(1)只是返回第一个括号分组内匹配的文本’mobile’。通过使用管道字符和分组括号,可以指定几种可选的模式,让正则表达式去匹配。

  如果需要匹配真正的管道字符,就用倒斜杠转义,即\|

用问号实现可选匹配

  我们可以让匹配的模式是可选的。就是说,不论这段文本在不在,正则表达式都会认为匹配。字符?表明它前面的分组在这个模式是可选的。例如:

1
2
3
4
5
6
7
8
import re

batRegex = re.compile(r'Bat(wo)?man')
mo1 = batRegex.search('The Adventures of Batman')
print(mo1.group())

mo2 = batRegex.search('The Adventures of Batwoman')
print(mo2.group())

  正则表达式中的(wo)?部分表明,模式wo是可选的分组。该正则表达式匹配的文本中,wo将出现零次或多次。

正则表达式用法总结

  正则表达式还有许多表示法,就不一一全部演示了,这里给出常用的用法:

  • ?匹配零次或一次前面的分组。
  • *匹配零次或多次的分组。
  • +匹配一次或多次前面的分组。
  • {n}匹配n次前面的分组。
  • {n,}匹配n次或更多次前面的分组。
  • {,m}匹配零次到m次前面的分组。
  • {n,m}匹配至少n次、至多m次前面的分组。
  • {n,m}?或*?或+?对前面的分组进行非贪心匹配。
  • ^begin意味着字符串必须以begin开头。
  • end$意味着字符串必须以end结尾。
  • .匹配所有字符,换行符除外。
  • \d\w\s分别匹配数字、单词和空格。
  • \D\W\S分别匹配除数字、单词和空格以外的所有字符。
  • [abc]匹配方括号在内的任意字符,诸如a、b或c。
  • abc匹配不在方括号内的任意字符。

正则表达式应用

利用正则表达式计算词频

  我们可以利用正则表达式来操作数据,下面的代码是利用正则表达式给出网页中使用频率最高的十个词。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import urllib.request
import re


def get_word_count(url):
with urllib.request.urlopen(url) as doc:
word_count = {}
# 得到这个页面的html文档
html = doc.read()
# 匹配单词,不区分大小写
tex_regex = re.compile(r'[a-zA-Z]{2,}')
tex = tex_regex.findall(str(html))
for word in tex:
if word in word_count:
word_count[word] += 1
else:
word_count[word] = 1
# 对匹配到的结果进行排序,items()方法得到这个字典的元组列表,lambda的用法是lambda x:y,x是传入值,y是返回值。
# key主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。item[1]就指定了根据字典中的值进行排序
# reverse是指定排序方式为升序或降序,True为升序,默认为降序
list_sorted = sorted(word_count.items(), key=lambda item: item[1], reverse=True)
for i in range(10):
# 最后打印前十的单词和次数
print(list_sorted[i])


get_word_count("http://www.baidu.com")

最后得到结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
('px', 1954)

('background', 428)

('icon', 353)

('position', 345)

('border', 334)

('width', 334)

('color', 327)

('height', 304)

('margin', 231)

('font', 212)