py 第二十五天 正则表达式1:一些元字符

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
python中使用re模块来实现

>>> import re

一、简单模式

1、字符串匹配

大多数字母和字符会匹配它们自身。举个例子,正则表达式 FishC将完全匹配字符串 "FishC"。当然这个规则也有例外。有少数特殊的字符我们称之为元字符(metacharacter),它们并不能匹配自身,它们定义了字符类、子组匹配和模式重复次数等。
元字符:. ^ $ * + ? { } [ ] \ | ( )

2、[ ] 字符类

它们指定一个字符类用于存放你需要匹配的字符集合。可以单独列出需要匹配的字符,也可以通过两个字符和一个横杆 - 指定匹配的范围。例如 [abc]会匹配字符 a,b 或 c[a-c]可以实现相同的功能。后者使用范围来表示与前者相同的字符集合。如果你想只匹配小写字母,你的 RE 可以写成 [a-z]

>>> re.search(r'[a-z]','I love 123 fishc.com!')
<re.Match object; span=(2, 3), match='l'>
>>> re.search(r'[0-9]','I love 123 fishc.com!')
<re.Match object; span=(7, 8), match='1'>
>>> re.search(r'[2-9]','I love 123 fishc.com!')
<re.Match object; span=(8, 9), match='2'>

元字符在方括号中不会触发“特殊功能”,在字符类中,它们只匹配自身。例如 [akm$] 会匹配任何字符'a','k','m' 或 '$''$' 是一个元字符,但在方括号中它不表示特殊含义,它只匹配'$' 字符本身。

>>> re.search(r'[.]','fish.com')
<re.Match object; span=(4, 5), match='.'>

你还可以匹配方括号中未列出的所有其他字符。做法是在类的开头添加一个脱字符号 ^ ,例如[^5] 会匹配除了 '5' 之外的任何字符

>>> re.search(r'[aeiou]','i love 123 fishc.com!')
<re.Match object; span=(0, 1), match='i'>
>>> re.search(r'[aeiou]','I love 123 fishc.com!')
<re.Match object; span=(3, 4), match='o'>    #正则表达式区分大小写
3、\

跟 Python 的字符串规则一样,如果在反斜杠后边紧跟着一个元字符,那么元字符的“特殊功能”也不会被触发。例如你需要匹配符号 [\,你可以在它们前面加上一个反斜杠,以消除它们的特殊功能:\[\\
反斜杠后边跟一些字符还可以表示特殊的意义,例如表示十进制数字,表示所有的字母或者表示非空白的字符集合。
\w匹配任何单词字符。如果正则表达式以字节的形式表示,这相当于字符类[a-zA-Z0-9_];如果正则表达式是一个字符串,\w 会匹配所有Unicode 数据库(unicodedata 模块提供)中标记为字母的字符。你可以在编译正则表达式的时候,通过提供re.ASCII 表示进一步限制 \w的定义。
re.ASCII 标志使得 \w 只能匹配 ASCII 字符,不要忘了,Python3 是 Unicode 的。

特殊字符 含义
\d 匹配任何十进制数字;相当于类 [0-9]
\D 与 \d 相反,匹配任何非十进制数字的字符;相当于类 [^0-9]
\s 匹配任何空白字符(包含空格、换行符、制表符等);相当于类 [ \t\n\r\f\v]
\S 与 \s 相反,匹配任何非空白字符;相当于类 [^ \t\n\r\f\v]
\w 匹配任何单词字符
\W 于 \w 相反
\b 匹配单词的开始或结束
\B 与 \b 相反
>>> re.search(r'\d','i love 123 fishc.com!')
<re.Match object; span=(7, 8), match='1'>
>>> re.search(r'\d\d\d','i love 123 fishc.com!')
<re.Match object; span=(7, 10), match='123'>

它们可以包含在一个字符类中,并且一样拥有特殊含义。例如[\s,.]是一个字符类,它将匹配任何空白字符(/s 的特殊含义),',' 或 '.'。

4、.

匹配除了换行符以外的任何字符。如果设置了re.DOTALL标志,.将匹配包括换行符在内的任何字符。

>>> import re
>>> re.search('fishc','i love fishc.com')
<re.Match object; span=(7, 12), match='fishc'>
>>> re.search(r'.','i love fishc.com')
<re.Match object; span=(0, 1), match='i'>
>>> re.search(r'fish.','i love fishc.com!')
<re.Match object; span=(7, 12), match='fishc'>
>>> re.search(r'\.','i love fishc.com!')
<re.Match object; span=(12, 13), match='.'>

二、重复的事情

正则表达式有另一个强大的功能,就是你可以指定 RE 部分被重复的次数。

1、*

它用于指定前一个字符匹配零次或者多次。
例如 ca*t将匹配 ct(0 个字符 a),cat(1 个字符 a),caaat(3 个字符 a),等等。需要注意的是,由于受到 C 语言的 int 类型大小的内部限制,正则表达式引擎会限制字符 'a' 的重复个数不超过 20 亿个

正则表达式默认的重复规则是贪婪的,当你重复匹配一个 RE 时,匹配引擎会尝试尽可能多的去匹配。直到 RE 不匹配或者到了结尾,匹配引擎就会回退一个字符,然后再继续尝试匹配。

我们通过例子一步步的给大家讲解什么叫“贪婪”:先考虑一下表达式 a[bcd]*b,首先需要匹配字符 'a',然后是零个到多个 [bcd],最后以'b'结尾。那现在想象一下,这个 RE 匹配字符串 abcbd 会怎样?

步骤 匹配 说明
1 a 匹配 RE 的第一个字符 'a'
2 abcbd 引擎在符合规则的情况下尽可能地匹配 [bcd]*,直到该字符串的结尾
3 失败 引擎尝试匹配 RE 最后一个字符 'b',但当前位置已经是字符串的结尾,所以失败告终
4 abcb 回退,所以 [bcd]* 匹配少一个字符
5 失败 再一次尝试匹配 RE 最后一个字符 'b',但字符串最后一个字符是 'd',所以失败告终
6 abc 再次回退,所以 [bcd]* 这次只匹配 'bc'
7 abcb 再一次尝试匹配字符 'b',这一次字符串当前位置指向的字符正好是 'b',匹配成功

* == {0,无穷}

2、+

用于指定前一个字符匹配一次或者多次。
要特别注意 *+ 的区别:*匹配的是零次或者多次,所以被重复的内容可能压根儿不会出现;+至少需要出现一次。例如 ca+t 会匹配 catcaaat,但不会匹配 ct
+ == {1,无穷}

3、?

用于指定前一个字符匹配零次或者一次。你可以这么想,它的作用就是把某种东西标志位可选的。例如 小?甲鱼 可以匹配 小甲鱼,也可以匹配 甲鱼
? == {0,1}

4、{}

最灵活的应该是元字符 {m,n}(m 和 n 都是十进制整数),它的含义是前一个字符必须匹配 m 次到 n 次之间。
例如a/{1,3}b会匹配a/ba//ba///b。但不会匹配 ab(没有斜杠);也不会匹配 a////b(斜杠超过三个)。
你可以省略 m 或者 n,这样的话,引擎会假定一个合理的值代替。省略 m,将被解释为下限 0;省略 n 则会被解释为无穷大(事实上是上边我们提到的 20 亿)
如果是 {,n} 相当于{0,n};如果是 {m,} 相当于 {m,+无穷};如果是 {n},则是重复前一个字符 n 次。

>>> re.search(r'ab{3}c','abbbc')
<re.Match object; span=(0, 5), match='abbbc'>
>>> re.search(r'ab{3,10}c','abbbbbc')
<re.Match object; span=(0, 7), match='abbbbbc'>
>>> re.search('(ab){3}','ababab')
<re.Match object; span=(0, 6), match='ababab'>

另外还有一个超容易出错的是写成 {m, n},看着挺美,但注意,正则表达式里边不能随意添加空格,不然会改变原来的含义。

https://fishc.com.cn/forum.php?mod=viewthread&tid=57073&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403 原文来自鱼C小甲鱼

tips:
1、如何取0-255之间的数字:

>>> re.search(r'[01]\d\d|2[0-4]\d|25[0-5]','188')
<re.Match object; span=(0, 3), match='188'>

先考虑百位,如果百位是1或0的话,后面两位可以是任意数字。如果百位是2,考虑十位,如果十位是0-4的话,个位可以是任意数字。最后考虑十位是5的情况,个位只能取0-5
2、正则表达式匹配IP地址
IP地址分为4段,以点号分隔。要对IP地址进行匹配,首先要对其进行分析,分成如下部分,分别进行匹配:

区间 表示
0-9 \d
10-99 [1-9]\d
100-199 [1]\d\d
200-249 2[0-4]\d
250-255 25[0-5]

故每一段可以表示为:\d|[1-9]\d|[1]\d\d|2[0-4]\d|25[0-5]
前三段可以表示为:((\d|[1-9]\d|[1]\d\d|2[0-4]\d|25[0-5])\.){3}
加上最后一段,IP地址的表达式为 ((\d|[1-9]\d|[1]\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|[1]\d\d|2[0-4]\d|25[0-5])
但是该方法存在BUG,最后一段,如果匹配满足第一个\d,就会停止匹配

>>> import re
>>> res = re.compile('((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])')
>>> res.search('255.255.255.255')
<re.Match object; span=(0, 13), match='255.255.255.2'>

解决方法是:
1、把三位数移到前面

res = re.compile('((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)')
>>> res.search('255.255.255.255')
<re.Match object; span=(0, 15), match='255.255.255.255'>

2、在末尾加上$,$表示匹配至字符串末尾

>>> res = re.compile('((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$')
>>> res.search('255.255.255.255')
<re.Match object; span=(0, 15), match='255.255.255.255'>
>>> res.search('188.123.1.2')
<re.Match object; span=(0, 11), match='188.123.1.2'>

3、启用非贪婪模式

>>> s = '<html><title>love fishc </title></html>'
>>> re.search(r'<.+>',s)
<re.Match object; span=(0, 39), match='<html><title>love fishc </title></html>'>
>>> re.search(r'<.+?>',s)
<re.Match object; span=(0, 6), match='<html>'>

加上?就可以启用非贪婪模式

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