我们在做机器学习项目的时候,很大部分的精力都在做数据的整理,不管是用爬虫在网上爬取数据还是对已有的数据进行整理,往往需要对一些特定的字符串进行处理,正则表达式则是进行数据处理的利器。
一、什么是正则表达式
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。
比如,验证一个字符串是否是符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。
比如通过正则规则: (^[\w-]+(.[\w-]+)*@[\w-]+(.[\w-]+)+$)
即可匹配一个字符串是否是标准邮箱格式。但如果不使用正则,使用if else来对字符串做判断就非常困难了。
二、Python中如何使用正则表达式
正则表达式的使用分两部分,一是编写模式(规则)文本,也就是正则表达式;二是通过具体的编程语言来使用正则表达式进行使用,在Python语言中通过re 模块使 Python 语言拥有全部的正则表达式功能。
1、正则表达式模式的编写
模式字符串使用特殊的语法来表示一个正则表达,由一些普通字符和一些元字符(metacharacters)组成。
字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。
多数字母和数字前加一个反斜杠时会拥有不同的含义。
标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
反斜杠本身需要使用反斜杠转义。
普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,在最简单的情况下,一个正则表达式看上去就是一个普通的字符串。例如,正则表达式"xiejava"
中没有包含任何元字符,它可以匹配"xiejava"
和"xiejava1018"
等字符串,但是不能匹配"XieJava"
。
在刚刚我们只是进行了基础的字符串匹配,正则最强大的功能在于元字符匹配规则,常见的元字符匹配规则如下:
- 单字符匹配:
字符 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n) , .匹配点本身 |
[ ] | 匹配[]中列举的字符 |
\d | 匹配任意数字,等价于 [0-9] |
\D | 匹配任意非数字 |
\s | 匹配任意空白字符,即空格、tab键,等价于 [\t\n\r\f] |
\S | 匹配任意非空字符 |
\w | 匹配单词字符,即a-z、A-Z、0-9、 |
\W | 匹配非单词字符 |
- 数量匹配:
字符 | 功能 |
---|---|
* | 匹配前一个规则的字符出现0至无数次 |
+ | 匹配前一个规则的字符出现1至无数次 |
? | 匹配前一个规则的字符出现0次或1次 |
{m} | 匹配前一个规则的字符出现m次 |
{m,} | 匹配前一个规则的字符出现最少m次 |
{m,n} | 匹配前一个规则的字符出现m到n次 |
- 边界匹配:
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界 |
- 分组匹配:
字符 | 功能 | |
---|---|---|
\ | 匹配左右任意一个表达式 | |
() | 将括号中字符作为一个分组 |
示例:
匹配账号,只能由字母和数字组成,长度限制6到10位
规则为: [0-9a-zA-Z]{6, 10}$
匹配QQ号,要求纯数字,长度4-10,第一位不为0
规则为:[1-9][0-9]{4, 10}
[1-9]匹配第一位,[0-9]匹配后面4到10位
匹配邮箱地址,只允许qq、163、189这三种邮箱地址
规则为:[a-zA-Z0-9_.]+@[qq|163|189]+[.a-zA-Z]+
2、Python中re模块的基础使用
Python中re模块常用的正则表达式包括以下四个主要方法:
- 使用match()进行准确匹配
re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match() 就返回 none。
import re
account_pattern='^[0-9a-zA-Z]{6,9}$'
account_str1='xiejava1018'
account_str2='xiejava'
account_str3='xie'
result1=re.match(account_pattern,account_str1)
print(result1)
result2=re.match(account_pattern,account_str2)
print(result2)
result3=re.match(account_pattern,account_str3)
print(result3)
运行结果为:
None
<re.Match object; span=(0, 7), match='xiejava'>
None
匹配成功 re.match 方法返回一个匹配的对象,否则返回 None。
我们可以使用 group(num) 或 groups() 匹配对象函数来获取匹配表达式。
if result2:
print(result2.group())
运行结果为:
xiejava
- 使用search()首次匹配查找
match()方法是从字符串的开头开始匹配,一旦起始位置不匹配,match() 就返回 none,整个匹配就失败了,它更适合用来检验某个字符串是否符合某个正则表达式的规则。常用来做格式的校验。如要实现在一段文本中查找符合正则表达式的字符串,可以用re.search(),它扫描整个字符串并返回第一个成功的匹配。
qq_pattern = '[1-9][0-9]{4,10}'
qq_str1='我的QQ号是:4002692和3641250'
result_qq_match=re.match(qq_pattern,qq_str1)
print(result_qq_match)
result_qq_search=re.search(qq_pattern,qq_str1)
print(result_qq_search)
if result_qq_search:
print(result_qq_search.group())
运行结果:
None
<re.Match object; span=(7, 14), match='4002692'>
4002692
在这可以看出,match()因为是起始位置匹配所以QQ号4002692没有匹配上,而search()它是扫描整个字符串找到第一个匹配成功的就返回了,实际上在这个例子中还有个3641250的QQ号。如果要找到所有的匹配就要用到re.findall()方法了。
- 使用findall()所有匹配查找
findall()在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次 findall 匹配所有。
result_qq_findall=re.findall(qq_pattern,qq_str1)
print(result_qq_findall)
运行结果:
['4002692', '3641250']
可以看到通过re.findall()方法匹配到了两个QQ号。
- 使用sub()匹配替换
在做数据处理的时候,经常碰到的场景是找到某个匹配项,替换成一个新的字符串。这时就可以用到re.sub()进行匹配替换了。
email_pattern='[a-zA-Z0-9_\.]+@[qq|163|189]+[\.a-zA-Z]+'
email_str1='我的email是:xiejava@qq.com、xiejava1018@163.com、xiejava@189.com、xiejava@sohu.com'
result_email_findall=re.findall(email_pattern,email_str1)
print(result_email_findall)
email_str2=re.sub('[a-zA-Z0-9_\.]+@','****@',email_str1)
print(email_str2)
运行结果:
['xiejava@qq.com', 'xiejava1018@163.com', 'xiejava@189.com']
我的email是:****@qq.com、****@163.com、****@189.com、****@sohu.com
在这里可以看到,我们通过re.findall()匹配到了qq、163、189的邮箱。通过re.sub()将所有的邮箱的账号匹配后替换成了**进行脱敏处理。
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
使用实例如下:
match_pattern='xiejava'
account_str1='XieJava is xiejava'
result1=re.match(match_pattern,account_str1)
print(result1)
result2=re.match(match_pattern,account_str1,re.I)
print(result2)
运行结果:
None
<re.Match object; span=(0, 7), match='XieJava'>
可以看到在没有指定re.I的时候没有匹配到XieJava,指定re.I后使匹配对大小写不敏感,可以匹配到Xiejava
三、正则表达式的应用
给定一个正则表达式和另一个字符串,我们可以达到如下的目的:
判断给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”)
应用场景为格式效验,如身份证、Email、手机号码的数据效验等。可以通过正则表达式,从字符串中获取我们想要的特定部分
应用场景如:爬虫程序爬取数据时进行信息的提取,从文本中提取特定的部分。可以通过正则表示式进行查找替换,将字符串中符合匹配条件的项替换成特定的字符
应用场景如:机器学习中的数据清洗整理,将某些数据进行查找替换等。