python正则表达式入门

简介: python正则表达式入门

引言

如果我们希望在字符串里面去寻找电话号码,电话号码的模式一般是这样的:3个数字+一个短横线+3个数字+一个短横线+4个数字。比如415-555-4242

如果我们希望通过一个函数来实现检查字符串是否匹配模式,它会返回True或者False,代码如下:

def isPhoneNumber(text):
    if len(text)!=12:
        return False
    #isdigit()函数用来判断一个字符串是否全是数字
    for i in range(0,12):
        if i==3 or i==7:
           if text[i] != '-':
            return False
        else:
            if not text[i].isdigit():
                return False
    return True
for i in range(3):
    s=input("请输入要匹配的电话号码:")
    if isPhoneNumber(s):
        print("{}是合法的".format(s))
    else:
        print("{}是不合法的".format(s))

运行是这样的:

请输入要匹配的电话号码:123-456-7890
123-456-7890是合法的
请输入要匹配的电话号码:12-354678-199
12-354678-199是不合法的
请输入要匹配的电话号码:1234665758889-0
1234665758889-0是不合法的

正则表达式查找文本模版

前言

前面的电话号码查找程序能工作,但是它只能实现对一种电话号码模式的匹配,象123.456.7890/(123)456-7890也是合理的电话号码模式,当我们对它们进行匹配的时候就要去增加逻辑,但是有没有更简单的方式呢?这就是我们今天要提到的正则表达式

常见的匹配字符

  • #### . 通配符,匹配任意1个字符(除了\n换行)
    [ ] 匹配[ ]中列举的字符,里面的元素之间存在“或”的关系,[abc]只能匹配a或b或c
    \d 匹配数字,等价于[0,9]
    \D 匹配非数字,等价于[^0-9]
    \s 匹配空白,常见的空白符的形式有空格、制表符 (\t)、换行 (\n) 和回车 (\r)
    \S 匹配非空白
    \w 匹配非特殊字符,即a-z、A-Z、0-9、_、中文
    \W 匹配特殊字符

创建正则表达式对象

python里面有关于正则表达式的函数都在re模块,它在我们安装的python环境中,所以我们只需要导入即可:

import re

我们可以向re.complie()中传入一个字符串,表示正则表达式,它将返回一个Regex模式对象,例如如果要创建一个Regex对象来匹配电话号码,只需要:

phoneNumber=re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')

匹配Regex对象

Regex对象的searc()方法查找输入的字符串,以寻找该正则表达式的所有匹配。如果未找到,则返回None,如果找到了该模式,search将返回一个Match对象,而它里面又一个**group()**方法返回所有实际上匹配的文本:

import re
phoneNumber=re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
mo=phoneNumber.search("My name is 147-907-7916")
print("The phone number:{}".format(mo.group()))

输出为:

The phone number:147-907-7916

利用正则表达式实现匹配更多模式

上面我们介绍了python创建和查找正则表达式对象的基本步骤,现在让我们来尝试一些更加强大的匹配模式

利用括号分组

假如我们想把电话号码的区号从代码里面分离,可以添加括号在代码里面添加分组然后从分组里面获得匹配文本。

正则表达式的第几组括号就是第几组,向group输入n就可以获得第n组获得的文本信息,而传0或不传就是获得全部的匹配文本,示例如下:

phonenumber2=re.compile(r'(\d\d\d)-(\d\d\d\d-\d\d\d\d)')
mo=phonenumber2.search("My phoneNumber is 147-9037-7916")
areaCode,mainNumber=mo.groups()
print("区号:{},电话号码:{},完整号码:{}".format(areaCode,mainNumber,mo.group()))

输出为:

区号:147,电话号码:9037-7916,完整号码:147-9037-7916

注意:括号在正则表达式里面有特殊的含义,如果我们在匹配的时候想匹配括号,我们可以使用\符号对括号进行字符转义,示例如下:

phonenumber2=re.compile(r'(\d\d\d)-(\(\d\d\d\d-\d\d\d\d\))')
mo=phonenumber2.search("My phoneNumber is 147-(9037-7916)")
areaCode,mainNumber=mo.groups()
print("区号:{},电话号码:{},完整号码:{}".format(areaCode,mainNumber,mo.group()))

输出为:

区号:147,电话号码:(9037-7916),完整号码:147-(9037-7916)

而在后面我们会遇到其他有特殊含义的字符,如果我们在匹配的时候想匹配它们,我们可以也使用\符号对它们进行字符转义。

用管道匹配多个分组

我们将字符**|**称为“管道”,当我们希望匹配许多表达式中的一个就可以使用它,如下:

heroRegex1=re.compile(r'\d\d\d-\d\d-\d\d|\d\d\d\d-\d\d')
mo1=heroRegex1.search("456-78-90 9876-54 3456-78 456-78 12-3456-78 987-6-54 123-45-67 1234-56 123-45-678 987-12-34")
print(mo1.group())

输出结果为:

456-78-90

我们尝试交换匹配文本的数据:

heroRegex2=re.compile(r'\d\d\d\d-\d\d|\d\d\d-\d\d-\d\d')
mo2=heroRegex2.search("9876-54 456-78-90  3456-78 456-78 12-3456-78 987-6-54 123-45-67 1234-56 123-45-678 987-12-34")
print(mo2.group())

输出结果为:

9876-54

我们也可以将它与上面说的括号混合使用:

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

运行结果:

Batmobile
mobile

上面就实现对多个拥有相同的前缀如何只指定一次前缀来指定几种可选的模式来让正则表达式来匹配。

用问号实现可选匹配

有时候,一些文本我们在匹配的时候希望它存在,但没有其实也可以接受,这时候我们可以使用字符**?**来表示前面的分组在该模式中是可选的,如下面的这一串代码:

answerFinder=re.compile(r'Bat(wo)?man')
mo=answerFinder.search("The Batman")
mo1=answerFinder.search("The Batwoman")
print("the mo:{}\nthe mo1:{}".format(mo.group(),mo1.group()))

输出为:

the mo:Batman
the mo1:Batwoman

如上**(wo)?表示wo是可选,无论是Batman还是Batwoman**都可以匹配成功

利用*匹配零次或多次

表示匹配0次或多次,即****前面的分组可以出现任意次,示例;

answerFinder=re.compile(r'Bat(wo)*man')
mo=answerFinder.search("The Batman")
mo1=answerFinder.search("The Batwowowowowoman")
print("the mo:{}\nthe mo1:{}".format(mo.group(),mo1.group()))

输出:

the mo:Batman
the mo1:Batwowowowowoman

利用加号匹配一次或多次

相比较于的0次或多次,+则是要求分组必须要出现一次,其余与基本一致。

利用{}匹配特定次数

如果想要一个分组重复指定次数,就在正则表达式中该分组后面跟上**{}**包围的数字。例如(\d){4}表示将匹配四个整数

除了在**{}**填入一个数字以外,还可以指定一个范围:

# 匹配两次
answerFinder=re.compile(r'Bat(wo){2}man')
#匹配至少两次
answerFinder=re.compile(r'Bat(wo){2,}man')
#匹配至多两次
answerFinder=re.compile(r'Bat(wo){,2}man')

示例:

phoneNumber1=re.compile(r'(\d){3}-(\d){3}-(\d){4}')
mo=phoneNumber1.search("My name is 147-907-7916")
print("The phone number:{}".format(mo.group()))

输出为:

The phone number:147-907-7916

贪心与非贪心匹配

如果我们匹配字符串**”hahahahaha“** (ha{3,5)可以匹配3个,4个或5个实例,但是当我们去匹配的时候就会发现只会返回hahahahaha,这是因为python的正则表达式默认是**”贪心“的,这表示有多个结果的时候,会默认选择尽可能长的字符串,但我们也可以在{}后面加上?**让其变为非贪心模式,示例如下:

answerFinder1=re.compile(r'(ha){3,5}')
answerFinder2=re.compile(r'(ha){3,5}?')
mo1=answerFinder1.search("hahahahaha")
mo2=answerFinder2.search("hahahahaha")
print("the mo1:{}\nthe mo2:{}".format(mo1.group(),mo2.group()))

输出为:

the mo1:hahahahaha
the mo2:hahaha

findall()方法

除了search()方法,Regex对象还有一个findall()方法,search会返回一个Match对象,包含被查找字符串中”第一次“匹配的文本;而findall()方法将返回一组字符串,包含被查找字符串里面所有匹配文本,而且它返回的不是一个Match对象,而是一个字符串列表(如果在正则表达式中没有分组),如果有分组,那返回的就是一个元组的列表,其中的项就是正则表达式中每个分组的匹配字符串,如下:

heroRegex2=re.compile(r'\d\d\d\d-\d\d')
mo2=heroRegex2.findall("9876-54 456-78-90  3456-78 456-78 12-3456-78 987-6-54 123-45-67 1234-56 123-45-678 987-12-34")
print(mo2)
heroRegex2=re.compile(r'(\d\d\d\d)-(\d\d)')
mo1=heroRegex2.findall("9876-54 456-78-90  3456-78 456-78 12-3456-78 987-6-54 123-45-67 1234-56 123-45-678 987-12-34")
print(mo1)

输出为:

['9876-54', '3456-78', '3456-78', '1234-56']
[('9876', '54'), ('3456', '78'), ('3456', '78'), ('1234', '56')]

创建自己的字符分类

在前面我们介绍了常见的匹配字符,但是我们不得不承认这些字符的范围太过宽泛,我们可以使用方括号来定义自己的字符分类,例如我们可以使用**[aoeiuAOEIU]**来匹配所有的元音字符 ,

vowelRegx=re.compile(r'[aeiouAEIOU]')
mo=vowelRegx.findall("The quick brown fox jumped over the lazy dog")
print(mo)

输出:

['e', 'u', 'i', 'o', 'o', 'u', 'e', 'o', 'e', 'e', 'a', 'o']

插入字符^与美元字符$

我们可以在正则表达式开始处使用^,表示匹配必须发生在被查找文本开始处。同样的,我们可以在正则表达式的末尾加上, 表示必须以这个正则表达式的模式结束,我们可以同时使 用 和 ,表示必须以这个正则表达式的模式结束,我们可以同时使用^和,表示必须以这个正则表达式的模式结束,我们可以同时使,表示整个字符串必须匹配该模式,而不是字符串的某个子集,示例如下:

beginwithHello=re.compile(r'^Hello')
mo1=beginwithHello.search("Hello,World")
mo2=beginwithHello.search("Python,Hello")
print("the mo1:{}\nthe mo2:{}".format(mo1.group(),mo2.group()))
endwithHello=re.compile(r'Hello$')
mo1=endwithHello.search("Hello,World")
mo2=endwithHello.search("Python,Hello")
print("the mo1:{}\nthe mo2:{}".format(mo1.group(),mo2.group()))

我们会发现会报错

Traceback (most recent call last):
  File "E:\python\typora源码放置处(python)\正则表达式.py", line 76, in <module>
    print("the mo1:{}\nthe mo2:{}".format(mo1.group(),mo2.group()))
                                                      ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'group'

修改一下

beginwithHello = re.compile(r'^Hello')
mo1 = beginwithHello.search("Hello,World")
mo2 = beginwithHello.search("Python,Hello")
print("the mo1:{}\n".format(mo1.group()))
endwithHello = re.compile(r'Hello$')
mo2 = endwithHello.search("Python,Hello")
print("the mo2:{}\n".format(mo2.group()))

输出为

the mo1:Hello
the mo2:Hello

通配字符

在正则表达式中,**.**字符称为“通配字符”它可以匹配除了换行符以外的所有字符。

利用点-星匹配所有字符

有时候想要匹配所有字符串,例如假定想要匹配字符串First Name,接下来是任意文本,然后Last Name,然后还是任意文本,示例:

nameRegex=re.compile(r'First Name:(.*) Last Name:(.*)')
mo=nameRegex.search("First Name:John Last Name:Doe")
print(mo.group(1))
print(mo.group(2))

输出为:

John
Doe

非贪心匹配

nonggreedyRegex=re.compile(r'<.*?>')
mo=nonggreedyRegex.search("<To be, or not to be> is a question")
print(mo.group())
<To be, or not to be>

用句号字符匹配换行符

点-星可以匹配换行符外的所有字符,当我们传入re.DOTALL作为**re.compile()**的第二个参数,继而让句点字符匹配所有字符,包括换行符,示例如下:

nonggreedyRegex1=re.compile(r'.*')
mo1=nonggreedyRegex1.search("To be, or not to be> is a question\nFirst Name:John Last Name:Doe\nThe quick brown fox jumped over the lazy dog")
print(mo1.group())
nonggreedyRegex2=re.compile(r'.*')
mo1=nonggreedyRegex2.search("To be, or not to be> is a question\nFirst Name:John Last Name:Doe\nThe quick brown fox jumped over the lazy dog")
print(mo2.group())

输出:

To be, or not to be is a question
To be, or not to be is a question
First Name:John Last Name:Doe
The quick brown fox jumped over the lazy dog

不区分大小

通常,正则表达式在匹配的时候会注意文本的大小写,而不关心它们的大小写的话,可以传入re.I或者re,IGNOREVASE作为**re.compile()**的第二个参数,示例:

robocup=re.compile(r'Robocup',re.I)
mo1=robocup.search("Robocup is a sport")
mo2=robocup.search("ROBOCUP is a sport")
mo3=robocup.search("ROboCUp is a sport")
print("{},{},{}".format(mo1.group(),mo2.group(),mo3.group()))

输出为:

Robocup,ROBOCUP,ROboCUp

用sub()方法替换字符串

正则表达式不仅可以找到文本模式,而且还能用新的文本替换掉这些模式,Regex对象的sub方法需要接受两个参数:

  1. 一个用来替换发现的匹配的字符串
  2. 正则表达式

示例:

nameRegex = re.compile(r'Agent\w+')
nameRegex.sub("fengxu", "Agent Alice gave the secret documents to Agent Bob")

有时候我们需要匹配文本表示作为替换字符串的一部分,在sub方法的第一个参数中,可以输入**\1,\2,\3表示在替换中输入分组1、2、3**的文本,示例:

agentNameRegex=re.compile(r'Agent (\w)\w*')
result=agentNameRegex.sub(r'\1****','Agent Alice told Agent Carol that Agent Eve knew Agent Bob was double agent')
print(result)

输出为:

A**** told C**** that E**** knew B**** was double agent

管理复杂的正则表达式

如果匹配的文本模式很简单,那么使用简单的正则表达式就足够了,但是匹配复杂的文本模式 ,正则表达式可能很长很复杂,这时候我们就要引用空白符与注释了,这时候我们可以输入re.VERBOSE作为第二个参数,如下:

phoneRegex=re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\S*(ext|x|ext.)\s*\d{2.5})?)')
phoneRegex2=re.compile('''(
    (\d{3}|\(\d{3}\))?           #area code
    (\s|-|\.)?                   #separator
    \d{3}                       #first 3 digits
    (\s|-|\.)                   #separator
    \d{4}                       # last digits
    (\S*(ext|x|ext.)\s*\d{2.5})?# extension
)''',re.VERBOSE)
相关文章
|
6天前
|
数据采集 存储 XML
Python爬虫定义入门知识
Python爬虫是用于自动化抓取互联网数据的程序。其基本概念包括爬虫、请求、响应和解析。常用库有Requests、BeautifulSoup、Scrapy和Selenium。工作流程包括发送请求、接收响应、解析数据和存储数据。注意事项包括遵守Robots协议、避免过度请求、处理异常和确保数据合法性。Python爬虫强大而灵活,但使用时需遵守法律法规。
|
7天前
|
Python
深入理解Python装饰器:从入门到实践####
本文旨在通过简明扼要的方式,为读者揭开Python装饰器的神秘面纱,从基本概念、工作原理到实际应用场景进行全面解析。不同于常规的摘要仅概述内容概要,本文将直接以一段精炼代码示例开篇,展示装饰器如何优雅地增强函数功能,激发读者探索兴趣,随后深入探讨其背后的机制与高级用法。 ####
36 11
|
3天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
3天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
3天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
9天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
43 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
3天前
|
设计模式 缓存 开发框架
Python中的装饰器:从入门到实践####
本文深入探讨了Python中装饰器的工作原理与应用,通过具体案例展示了如何利用装饰器增强函数功能、提高代码复用性和可读性。读者将学习到装饰器的基本概念、实现方法及其在实际项目开发中的实用技巧。 ####
15 3
|
5天前
|
Python
在Python中,可以使用内置的`re`模块来处理正则表达式
在Python中,可以使用内置的`re`模块来处理正则表达式
18 5
|
6天前
|
机器学习/深度学习 数据采集 数据可视化
Python在数据科学中的应用:从入门到实践
本文旨在为读者提供一个Python在数据科学领域应用的全面概览。我们将从Python的基础语法开始,逐步深入到数据处理、分析和可视化的高级技术。文章不仅涵盖了Python中常用的数据科学库,如NumPy、Pandas和Matplotlib,还探讨了机器学习库Scikit-learn的使用。通过实际案例分析,本文将展示如何利用Python进行数据清洗、特征工程、模型训练和结果评估。此外,我们还将探讨Python在大数据处理中的应用,以及如何通过集成学习和深度学习技术来提升数据分析的准确性和效率。
|
5天前
|
机器学习/深度学习 数据挖掘 开发者
Python编程入门:理解基础语法与编写第一个程序
【10月更文挑战第37天】本文旨在为初学者提供Python编程的初步了解,通过简明的语言和直观的例子,引导读者掌握Python的基础语法,并完成一个简单的程序。我们将从变量、数据类型到控制结构,逐步展开讲解,确保即使是编程新手也能轻松跟上。文章末尾附有完整代码示例,供读者参考和实践。