Python知识点记录三(正则表达式)

正则表达式

正则表达式是一个特殊的字符序列,可以检测一个字符串是否与我们设定的字符序列相匹配。

一、findall()方法

re模块下的findall()方法可以查找对应字符/字符串

最简单的方法可以查找字符串中的特定字符串:

import re
a = 'C|C++|Java|C#|Python|Javascript'

r = re.findall('Python', a)   #返回一个包含结果的列表
print(r)
if len(r):
    print('字符串中包含Python。')
else:
    print('字符串中不包含Python。')

二、元字符和普通字符

对于上例findall()方法中的第一个实参'Python'即为普通字符,即表示字符本意的字符。

元字符:包括\d匹配0-9中的数字;\D匹配一个非数字字符,即[^0-9];等等。

import re
a = 'C0C++7Java8C#9Python6Javascrip'
#通过'\d'匹配数字字符
r = re.findall('\d',a)
print(r)
#通过'\D'匹配数字字符
r = re.findall('\D',a)
print(r)

三、字符集

正则表达式中通过字符集设定字符的可能值,字符集通过[]表示。

如[0-9]表示0到9中的一个数字,即等同于元字符\d;[cf]字符集匹配c或者f;a[hc]b匹配一个三个字符的字符串,第一个字符为a,第二个字符为h或c,第三个字符为b;[^1]匹配所有不是1的字符。

import re

s0 = 'He1ll2o,Wor3ld.'
s1 = 'abc, acc1, adc, aec, a1efc, ahc'
#匹配为e或o的字符
r0 = re.findall('[eo]', s0)
#匹配0-3之间的字符
r00 = re.findall('[0-3]', s0)
#匹配非小写字母的字符,且第二个字符为逗号
r01 = re.findall('[^a-z],',s1)
#匹配第一个字符为a,第三个字符为c,第二个字符为c或f的字符串
r1 = re.findall('a[cf]c', s1)

print(r0)
print(r00)
print(r01)
print(r1)

四、概括字符集

上述提及的\d等元字符可以理解为概况字符集,即概况描述某一类字符的集合,如\d也可以表示为[0-9]字符集。

元字符\D可以用字符集[^0-9]表示;

元字符\w表示匹配单词字符(字母,数字,下划线)可以用字符集[A-Za-z0-9_]表示;

元字符\W表示匹配非单词字符,即%¥#@此类,也包括空格,制表符,回车,换行等空白字符;

元字符\s表示匹配空白字符,即空格、回车、换行符等。

import re

a = '_&python# 2java8\n9@php'
#\d匹配一个数字字符
r = re.findall('\d', a)
#\D匹配一个非数字字符
r1 = re.findall('\D', a)
#\w匹配一个单词字符(数字或字母字符或下划线)
r2 = re.findall('\w', a)
#\W匹配一个非单词字符
r3 = re.findall('\W', a)
#\s匹配一个空白字符
r4 = re.findall('\s', a)

print(r)
print(r1)
print(r2)
print(r3)
print(r4)

五、数量词

对于以下代码:

import re
a = 'python 1111java678php'

r = re.findall('[a-zA-Z]', a)
print(r)

#输出:['p', 'y', 't', 'h', 'o', 'n', 'j', 'a', 'v', 'a', 'p', 'h', 'p']

因为参数正则表达式为一个字符集,仅匹配单个字符;如果需要以单词的形式作为输出的元素,则需要用到数量词这个方式。

import re
a = 'python 1111java678php'

r = re.findall('[a-z]{3,6}', a)
print(r)

#输出:['python', 'java', 'php']

参数中的[a-z]表示字符的匹配条件,{3,6}表示需要匹配的数量为3-6个,需要注意这里Python会默认采用贪婪的匹配方法,即如果已经匹配到3个字符后还会继续匹配,直到不再满足匹配条件,或达到匹配数量的最大值为止。

import re
a = 'python 1111java678php'

r = re.findall('[a-z]{3,6}', a)
print(r)

#输出:['python', 'java', 'php']

Python默认为贪婪匹配,如果要采用非贪婪模式匹配,则需要在数量词后加个'?':

import re
a = 'python 1111java678php'

#非贪婪匹配方式
r = re.findall('[a-z]{3,6}?', a)
print(r)

#输出:['pyt', 'hon', 'jav', 'php']

*除了上述的'{}'表示数量词之外,也可以通过‘*’表示匹配星号前面字符0次或1次或无限次,通过'+'表示匹配加号前面字符至少1次,通过'?'表示匹配问号前面字符0次或1次。。

import re

a = 'pytho0python1pythonn2'
#匹配*前面的n0次或1次或无限次
r = re.findall('python*', a)
#匹配+前面的n至少一次
r1 = re.findall('python+', a)
#匹配?前面的n0次或1次
r2 = re.findall('python?', a)
print(r)
print(r1)
print(r2)

#输出:
'''
['pytho', 'python', 'pythonn']
['python', 'pythonn']
['pytho', 'python', 'python']
'''

需要注意这里问号和前述数量词{}之后问号的区别。

通过数量词实现上例,注意对比:

import re

a = 'pytho0python1pythonn2'
#通过数量词{}取n1-2次,贪婪模式
r = re.findall('python{1,2}', a)
#通过数量词{}取n1-2次,非贪婪模式
r1 = re.findall('python{1,2}?', a)

print(r)
print(r1)


#输出:
'''
['python', 'pythonn']
['python', 'python']
'''

六、边界匹配

如果有一个数字字符串,需要判断是否为一个QQ号码,比如qq='10000001'。

QQ号码的判断规则(假设)为4-8位的数字,如果单纯通过数量词查找:r = re.findall('\d{4,8}', qq),对于本例也是可以查找匹配出的,但如果

qq = '1234567890',通过前面的正则表达式'\d{4,8}'也可以匹配到数字字符串,但显然这个QQ号码超出了8位,这里就需要通过边界匹配符来界定匹配范围:

import re

qq = ''
#QQ号为4-8位,检测是否为QQ号码
#通过边界匹配符'^...$'匹配整个字符串
#第一个^表示从整个字符串开头匹配4-8个数字
#最后一个$表示从整个字符串最末尾往前匹配4-8个数字
#两者都匹配到的才为最终匹配到的结果
r = re.findall('^\d{4,8}$', qq)
print(r)

再单独理解一下边界匹配符的第一个^和最后一个$的作用:

import re

qq = '123a4567890abchf'
#通过'^'匹配整个字符串开头的3-4个数字
r0 = re.findall('^\d{3,4}', qq)

#通过'$'匹配整个字符串末尾的3-4个字母
r1 = re.findall('[a-zA-Z]{3,4}$', qq)
print(r0)
print(r1)

'''
输出:
['123']
['bchf']
'''

七、组

前面数量词的使用中数量词针对的是其前面的单个字符,也可以通过组的概念将整组字符适用到数量词上:

import re

a = 'PythonPythonPythonPythonJSJSPython'
#找出是否含有连续三个Python,如果有则返回的是单个组,否则返回空列表
r = re.findall('(Python){3}', a)
#找出是否含有连续三个PYthon且连着两个JS
r0 = re.findall('(Python){3}(JS){2}', a)
#找出是否含有连续三个PYthon且连着三个JS
r1 = re.findall('(Python){3}(JS){3}', a)
print(r)
print(r0)
print(r1)


'''
输出:
['Python']
[('Python', 'JS')]
[]
'''

八、匹配模式参数

findall()还有第三个参数flags即指匹配模式,当参数值设为re.I则忽略正则表达式中的字母大小写;当参数值设为re.S则忽略

import re
language = 'PythonC#\nJavaPHP'
#忽略大小写的模式
r = re.findall('c#', language, re.I)
#‘.’本来表示除了换行之外的所有字符,re.S模式表示'.'可以匹配任意字符
#匹配整个字符串的最后两个字符
r0 = re.findall('.{2}$', language, re.S)
#匹配c#或C#及后面1-3个字符(非贪婪)
r1 = re.findall('c#.{1,3}?', language, re.I | re.S)
#匹配c#或C#及后面1-3个字符(贪婪)
r1 = re.findall('c#.{1,3}', language, re.I | re.S)
print(r)
print(r0)
print(r1)

'''
输出:
['C#']
['HP']
['C#\nJa']
'''

九、sub()/match()/search()函数

1、sub()函数可以实现查找后替换的功能。参数格式依此为正则表达式、需要匹配后替换的字符串(或函数)、原始字符串、匹配次数、模式参数。

import re

language = 'PythonC#JavaC#PHPC#'
r = re.sub('C#', 'Javascript', language)
print(r)

#输出:PythonJavascriptJavaJavascriptPHPJavascript

第四个参数表示匹配后替换的次数,默认为0,表示不限制次数的替换,如果设置为非0的某个数值,则表示替换的最大次数。

import re

language = 'PythonC#JavaC#PHPC#'
#无限次替换
r1 = re.sub('C#', 'Javascript', language, 0)
#最多1次替换
r2 = re.sub('C#', 'Javascript', language, 1)
#最多2次替换
r3 = re.sub('C#', 'Javascript', language, 2)
#对比字符串内置函数replace()
language0 = language.replace('C#', 'Javascript')
print(r1)
print(r2)
print(r3)
print(language0)

'''
输出:
PythonJavascriptJavaJavascriptPHPJavascript
PythonJavascriptJavaC#PHPC#
PythonJavascriptJavaJavascriptPHPC#
PythonJavascriptJavaJavascriptPHPJavascript
'''

第二个参数除了直接指定为字符串,也可以指定为某个函数,其功能是把正则匹配出的字符串作为该函数参数,返回的字符串作为替换的字符串。

这里需要注意正则参数传递的时候实际是一个正则对象,需要通过访问其group()获得真正匹配到的字符串。

import re

def convert(value):
    v = value.group()
    return v+'!'

language = 'PythonC#JavaC#PHPC#'

r = re.sub('C#', convert, language)
print(r)

#输出:PythonC#!JavaC#!PHPC#!

对于上例的convert()函数中,通过value.group()获取了匹配到的实际的字符串,这里value即是一个正则对象,除了group()函数,它还有span()方法,可以获取匹配到的字符串在原始字符串中的索引:

import re

def convert(value):
    #打印匹配到的字符在原始字符串中的索引
    print(value.span())
    v = value.group()
    return v + '!'

language = 'PythonC#JavaC#PHPC#'

r = re.sub('C#', convert, language)
print(r)

'''
输出:
(6, 8)  #匹配到的第一个C#对应索引为6,7
(12, 14)#匹配到的第一个C#对应索引为12,13
(17, 19)#匹配到的第一个C#对应索引为17,18
PythonC#!JavaC#!PHPC#!
'''
import re

s = 'A8C3721D86'

#大于等于6的数字替换成9,其余数字替换为0
def convert(value):
    n = int(value.group())
    if n >= 6:
        return ''
    else:
        return ''

r = re.sub('\d', convert, s)
print(r)

#输出:A9C0900D99
import re

s = 'A8C372R1X16D86F333P50F7N'

#剔除所有的单个数字字符
#两位数字大于50的替换为100,其余替换为0
def convert(value):
    n = int(value.group())
    if n < 10:
        return ''
    elif n < 50:
        return '0'
    else:
        return '100'

r = re.sub('\d{1,10}', convert, s)
print(r)

#输出:AC100RX0D100F100P100FN

2、search()/match()函数

(1)match()尝试从字符串开头去匹配,匹配成功则返回第一个匹配的字符串,返回一个Match对象;如果没有匹配到则返回None。

(2)search()将搜索整个字符串,直到找到第一个匹配的字符串,返回一个Match对象;如果没有匹配到则返回None。

import re

s = 'A8C3721D86'

r = re.match('\d', s)
print(r)

r0 = re.search('\d', s)
#通过对象的group()获取匹配到的字符串
print(r0.group())

需要特别注意的是:search()和match()函数只最多匹配一次,匹配到一次后不再继续查找匹配,而findall()函数会查找整个字符串范围。

3、分组

除了前述提到的数量重复的作用外,还可以通过分组括号指定需要匹配的字符:

import re

s = 'life is short,i use python,\n i love python'

r = re.search('life(.*)python', s, re.S)
#group()获取整个分组的匹配,即'life(.*)python'
print(r.group())
#也可以加下标0获取上述字符串
print(r.group(0))
#可以加下标1获取上述字符串中的小分组
print(r.group(1))
#groups()获取大分组下的小分组字符串,即上述(.*)
print(r.groups())

#通过findall()是最方便直观的:
r1 = re.findall('life(.*)python', s, re.S)
print(r1)
print()
import re

s = 'life is short,i use python, i love python'
#多个分组的情况
r = re.search('life(.*)python(.*)python', s, re.S)
#group()获取整个分组的匹配,即'life(.*)python(.*)python'
print(r.group(0))
#可以加下标1获取上述字符串中的第一个小分组
print(r.group(1))
#可以加下标2获取上述字符串中的第二个小分组
print(r.group(2))
#通过groups()获取小分组
print(r.groups())

#通过findall()是最方便直观的:
r1 = re.findall('life(.*)python(.*)python', s, re.S)
print(r1)

相关推荐