正则表达式学习笔记

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
在各大编程语言中都能发挥出很大的作用。每种语言也都有关于正则表达式模块的实现。
本文以Python举例说明。

Regular Expression

匹配单个字符

匹配纯文本

匹配纯文本其实我们一般不用正则表达式就可以,使用正则表达式只是能说明这个问题

#待匹配的文本
s = 'hello my name is ben'
print(re.findall('ben', s))
#待匹配的文本
s = 'hello my name is ben and my friend name is ben'
print('只返回第一个匹配结果为{}'.format(re.search('ben', s).span()))
print('返回所有匹配结果{}'.format(re.findall('ben', s)))

结果为:

['ben']
只返回第一个匹配结果为(17, 20)
返回所有匹配结果['ben', 'ben']

匹配任意字符

使用.匹配任意一个字符
.字符可以匹配任何单个的字符、字母、数字或者.本身

#待匹配的文本
s = 'sales1.txt' \
    'sales2.txt' \
    'salese.txt' \
    'nae.txt' \
    'sns.txt' \
    'sn.txt'

print('使用sale.匹配的所有结果为{}'.format(re.findall(r'sales.', s)))
print('使用.a.匹配的所有结果为{}'.format(re.findall(r'.a.', s)))
print('使用\转义匹配含有.的结果{}'.format(re.findall(r'.a.\.', s)))

结果为:

使用sale.匹配的所有结果为['sales1', 'sales2', 'salese']
使用.a.匹配的所有结果为['sal', 'sal', 'sal', 'nae']
使用\转义匹配含有.的结果['nae.']

匹配一组字符

匹配多个字符中的某一个

使用[ab]确定可选的字符
使用[0-9]确定可选的字符范围

s = 'na.txt' \
    'na2.txt' \
    'sa1.txt' \
    'ca1.txt' \
    'nam.txt'

print('匹配na或sa.txt的结果为{}'.format(re.findall(r'[ns]a.\.txt', s)))
print('限制a后面字符仅为数字的结果为{}'.format(re.findall(r'[ns]a[0-9]\.txt',s)))

结果为:

匹配na或sa.txt的结果为['na2.txt', 'sa1.txt', 'nam.txt']
限制a后面字符仅为数字的结果为['na2.txt', 'sa1.txt']
  • 注: 在同一个字符集合里页可以给出多个字符区间,会匹配到多个区间中的任何一个字符

取非匹配

我们使用正则表达式也不是都需要匹配集合其中的字符,有时也需要匹配非集合中的字符,在正则表达式中,我们可以使用^字符对集合进行取非操作

s = 'na.txt' \
    'na2.txt' \
    'sa1.txt' \
    'ca1.txt' \
    'nam.txt'

print('限制a后面字符不为数字的结果为{}'.format(re.findall(r'[ns]a[^0-9]\.txt', s)))

结果为:

限制a后面字符不为数字的结果为['nam.txt']

使用元字符

对特殊字符进行转义

使用\对元字符进行转义

s = 'list[0]   list[1]   list[2]'
print('使用\对中括号进行转义使其匹配{}'.format(re.findall(r'list\[[0-9]\]', s)))
path = '\home\\ben\sales'
print('将\替换成/为{}'.format(re.compile(r'\\').subn('/', path)))

结果为:

使用\对中括号进行转义使其匹配['list[0]', 'list[1]', 'list[2]']
将\替换成/为('/home/ben/sales', 3)

匹配特定的字符类别

空白元字符表

元字符 说明
[\b] 回退符
\f 换页符
\n 换行符
\r 回车符
\t 制表符
\v 垂直制表符

数字元字符

元字符 说明
\d 任何一个数字 (等价于[0-9])
\D 任何一个非数字(等价于[^0-9])

字母数字元字符

元字符 说明
\w 任何一个字母数字字符(大小写均可)或下划线字符 (等价于[a-zA-Z0-9_])
\W 任何一个非字母数字字符(等价于[^a-zA-Z0-9_])

空白字符元字符

元字符 说明
\s 任何一个空白字符 (等价于[\f\n\r\t\v])
\S 任何一个非空白字符(等价于[^\f\n\r\t\v])

重复匹配

匹配一个或多个字符

匹配同一个字符或(字符集合)的多次重复,只要在其后面加上一个+即可。
+匹配一个或多个字符(至少一个,不包含0个)

s = '1234545@qq.com   fdfsd@163.com test.sddf@163.com.cn'
print('匹配邮箱{}'.format(re.findall(r'[\w.]+@[\w.]+\.\w+', s)))

结果为;

匹配邮箱['1234545@qq.com', 'fdfsd@163.com']

匹配零个或多个字符

* 和+类似,只是可以允许匹配0个或多个的情况

匹配零个或一个字符

只能匹配一个字符(或字符集合)的零次或一次出现,最多不能超过一次。

匹配的重复次数

  1. 使用{}将数值写在其中来表示重复次数
s = '123abc   45abc'
print('匹配三个数字{}'.format(re.findall(r'\d{3}\w{3}', s)))

结果为:

匹配三个数字['123abc']
  1. 为重复匹配次数设定一个区间
    使用{a,b}的形式来确定区间,a为最少重复次数,b为最多重复次数
s = '4/8/03   ' \
    '10-6-2004   ' \
    '2/2/2   ' \
    '01-01-01   '

print('匹配所有格式的日期{}'.format(re.findall(r'\d{1,2}[-\/]\d{1,2}[-\/]\d{2,4}', s)))

结果为:

匹配所有格式的日期['4/8/03', '10-6-2004', '01-01-01']
  1. 匹配至少重复多少次
    使用{a,}的形式的形式来确定最少重复的次数

  2. 防止过度匹配
    *和+都是贪婪形元字符,使用他们在进行匹配时,会进可能的匹配而不是适可而止。他们会从一段文本的开头一直匹配到文本的末尾。在这种情况可能导致原本我们需要匹配多个结果,最后变成了一个结果,例如:

<B>TEST</B>AND <B>TEST1</B>

会被正则表达式<[Bb]>.*</[Bb]>匹配为一个整体的结果
此时应该使用懒惰型版本
常用的贪婪性元字符和它们的懒惰型版本

贪婪型元字符 懒惰型元字符
* *?
+ +?
{n,} {n,}?

用懒惰型元字符时的正则表达式应是<[Bb]>.*?</[Bb]>

位置匹配

单词边界

使用\b限定单词边界,用来匹配一个单词的开始或结尾
使用\B限定一个前后都不是单词边界的单词

s = 'the cat scattered his food all over the room color - red'
print('匹配单词边界{}'.format(re.findall(r'\bcat\b', s)))
print('不匹配单词边界{}'.format(re.findall(r'\-\B', s)))

结果为:

匹配单词边界['cat']
不匹配单词边界['-']

字符串边界

^限定字符串开头,$限定字符串的结尾

使用子表达式

()来将需要的子表达式划分出来,子表达式可以嵌套
|表示或,例如19|20表示为(19|20)匹配19或20

s = 'hello &nbsp;&nbsp; the world'
print('匹配两个&nbsp;的结果为{}'.format(re.findall(r'(&nbsp;){2,}', s)))

s = 'ping 12.159.46.200'
p = re.compile(r'(\d{1,3}\.){3}\d{1,3}')
print('使用子表达式匹配ip地址为{}'.format(p.findall(s)))

s = '1997     2005'
print('{}'.format(re.findall(r'(19|20)\d{2}', s)))

结果为:

匹配两个&nbsp;的结果为['&nbsp;&nbsp;']
使用子表达式匹配ip地址为['12.159.46.200']
['19', '20']

回溯引用,前后一致

使用\n代表模式里的第几个子表达式

s = 'this is and and or or not not'
print('{}'.format(re.findall(r'[ ]+(\w+)[ ]+\1', s)))

结果为:

['and', 'or', 'not']

注:

  • 回溯引用只能用来引用模式里的子表达式(用()括起来的正则表达式片段)
  • 回溯引用匹配通常用1开始技术(\1 \2等)。在许多实现里,第0个匹配(\0)可以用来代表整个正则表达式

前后查找

这种模式,包含的匹配本身并不返回,而是用于确定正确的匹配位置,它并不是匹配结果的一部分。
前后查找分为向前查找和向后查找,但常见的正则表达式实现都支持向前查找。所以只说明前者

向前查找

向前查找指定了一个必须匹配但不在结果中返回的模式。向前查找实际就是一个子表达式。
从语法上看,一个向前查找模式就是一个以?=开头的子表达式,需要匹配的文本跟在=的后面。

s = 'http://www.baidu.com \n' \
    'https://www.baidu.com \n' \
    'ftp://ftp.baidu.com    \n'

print('向前查找url中的协议名为{}'.format(re.findall(r'.{3,6}(?=:)', s)))

结果:

向前查找url中的协议名为['http', 'https', 'ftp']

向后查找

与向前查找相类似,查找出现在被匹配文本之前的字符(但不消费它),向后查找的操作符使?<=

嵌入条件

正则表达式里的条件要用?来定义

  • ?匹配前一个字符或表达式,如果它存在的话
  • ?= 和?<=匹配前面或后面的文本,如果它存在的话
  • 根据一个回溯引用来进行条件处理
  • 根据一个前后查找来进行条件处理

注:

  • 上述环境在ubuntu16.04 lts Python3.5中测试成功
  • 上述文字皆为个人看法,如有错误或建议请及时联系我
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容

  • 正则表达式练习题点击这里 创建一个正则表达式 使用一个正则表达式字面量,其由包含在斜杠之间的模式组成,如下所示: ...
    打铁大师阅读 1,235评论 0 6
  • 一.正则表达式匹配原则 占有字符和零宽度 在正则表达式匹配过程中,如果子表达式匹配到的是字符内容,并被保存在结果之...
    Coree阅读 522评论 0 0
  • 什么是正则表达式 Regular Expression使用单个字符串来描述,匹配一系列符合某个句法规则的字符串 说...
    打铁大师阅读 771评论 1 6
  • 推荐几个正则表达式编辑器 Debuggex :https://www.debuggex.com/ PyRegex:...
    木易林1阅读 11,437评论 9 151
  • 正则表达式的语法不再多说 正则RegExp类型有几个常用方法: exec() 该方法是专门为捕获组而设计的。 例:...
    灯不梨喵阅读 382评论 0 0