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)
相关文章
|
2月前
|
数据采集 监控 数据安全/隐私保护
Python正则表达式:用"模式密码"解锁复杂字符串
正则表达式是处理字符串的强大工具,本文以Python的`re`模块为核心,详细解析其原理与应用。从基础语法如字符类、量词到进阶技巧如贪婪匹配与预定义字符集,结合日志分析、数据清洗及网络爬虫等实战场景,展示正则表达式的强大功能。同时探讨性能优化策略(如预编译)和常见错误解决方案,帮助开发者高效掌握这一“瑞士军刀”。最后提醒,合理使用正则表达式,避免过度复杂化,追求简洁优雅的代码风格。
61 0
|
4月前
|
程序员 UED Python
Python入门:3.Python的输入和输出格式化
在 Python 编程中,输入与输出是程序与用户交互的核心部分。而输出格式化更是对程序表达能力的极大增强,可以让结果以清晰、美观且易读的方式呈现给用户。本文将深入探讨 Python 的输入与输出操作,特别是如何使用格式化方法来提升代码质量和可读性。
Python入门:3.Python的输入和输出格式化
|
4月前
|
机器学习/深度学习 人工智能 算法框架/工具
Python入门:1.Python介绍
Python是一种功能强大、易于学习和运行的解释型高级语言。由**Guido van Rossum**于1991年创建,Python以其简洁、易读和十分工程化的设计而带来了庞大的用户群体和丰富的应用场景。这个语言在全球范围内都被认为是**创新和效率的重要工具**。
Python入门:1.Python介绍
|
1月前
|
数据管理 开发者 Python
揭秘Python的__init__.py:从入门到精通的包管理艺术
__init__.py是Python包管理中的核心文件,既是包的身份标识,也是模块化设计的关键。本文从其历史演进、核心功能(如初始化、模块曝光控制和延迟加载)、高级应用场景(如兼容性适配、类型提示和插件架构)到最佳实践与常见陷阱,全面解析了__init__.py的作用与使用技巧。通过合理设计,开发者可构建优雅高效的包结构,助力Python代码质量提升。
110 10
|
6月前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。
|
2月前
|
数据采集 数据可视化 大数据
Python入门修炼:开启你在大数据世界的第一个脚本
Python入门修炼:开启你在大数据世界的第一个脚本
89 6
|
2月前
|
数据可视化 流计算 Python
Python创意爱心代码大全:从入门到高级的7种实现方式
本文分享了7种用Python实现爱心效果的方法,从简单的字符画到复杂的3D动画,涵盖多种技术和库。内容包括:基础字符爱心(一行代码实现)、Turtle动态绘图、Matplotlib数学函数绘图、3D旋转爱心、Pygame跳动动画、ASCII艺术终端显示以及Tkinter交互式GUI应用。每种方法各具特色,适合不同技术水平的读者学习和实践,是表达创意与心意的绝佳工具。
1075 0
|
4月前
|
开发者 Python
Python入门:8.Python中的函数
### 引言 在编写程序时,函数是一种强大的工具。它们可以将代码逻辑模块化,减少重复代码的编写,并提高程序的可读性和可维护性。无论是初学者还是资深开发者,深入理解函数的使用和设计都是编写高质量代码的基础。本文将从基础概念开始,逐步讲解 Python 中的函数及其高级特性。
Python入门:8.Python中的函数
|
4月前
|
存储 索引 Python
Python入门:6.深入解析Python中的序列
在 Python 中,**序列**是一种有序的数据结构,广泛应用于数据存储、操作和处理。序列的一个显著特点是支持通过**索引**访问数据。常见的序列类型包括字符串(`str`)、列表(`list`)和元组(`tuple`)。这些序列各有特点,既可以存储简单的字符,也可以存储复杂的对象。 为了帮助初学者掌握 Python 中的序列操作,本文将围绕**字符串**、**列表**和**元组**这三种序列类型,详细介绍其定义、常用方法和具体示例。
Python入门:6.深入解析Python中的序列
|
4月前
|
缓存 算法 数据处理
Python入门:9.递归函数和高阶函数
在 Python 编程中,函数是核心组成部分之一。递归函数和高阶函数是 Python 中两个非常重要的特性。递归函数帮助我们以更直观的方式处理重复性问题,而高阶函数通过函数作为参数或返回值,为代码增添了极大的灵活性和优雅性。无论是实现复杂的算法还是处理数据流,这些工具都在开发者的工具箱中扮演着重要角色。本文将从概念入手,逐步带你掌握递归函数、匿名函数(lambda)以及高阶函数的核心要领和应用技巧。
Python入门:9.递归函数和高阶函数

推荐镜像

更多