专栏首页python3python 历险记(六)— pytho

python 历险记(六)— pytho

目录

  • 引言
  • 什么是正则表达式?
  • 正则表达式有什么用?
  • 正则表达式的语法及使用实例
    • 正则表达式语法有哪些?
    • 这些正则到底该怎么用?
  • 小结
  • 参考文档
  • 系列文章列表

引言

刚接触正则表达式,我也曾被它们天书似的符号组合给吓住,但经过一段时间的深入学习,发现它并没有想象中那么可怕,只要多实践,多理解,也是可以轻松搞定的。 而且我发现带着问题去学习,求知欲会驱使着你往前走,不知不觉就懂了。 下面就是我在学习中提出的几个问题,在后面会依次进行讨论。由于正则表达式涉及到的内容确实非常多,分成两篇来阐述。

  1. 什么是正则表达式?
  2. 正则表达式可以干什么?
  3. 正则表达式的语法以及在 python 中这些语法是如何使用的?
  4. 正则表达式如何处理中文字符?
  5. python 的正则表达式库中有哪些重要的函数?

什么是正则表达式?

正则表达式使用单个字符串来描述,匹配一系列符合某个句法规则的字符串。 — 维基百科

先来划重点:

  1. 正则表达式的表现形式是 单个字符串
  2. 它用来执行匹配的动作
  3. 匹配的对象也是字符串

语言总是有些苍白的,必须要结合实例才能理解的更清楚,先来看一个例子:

>>> import re
>>>re.search(r'wo\w+d', 'hello world!')
<re.Match object; span=(6, 11), match='world'>
>>> 

这里先概略说明 re.search 方法:引入的 re 模块就是 python 的正则表达式模块,re.search 函数目的就是接受一个正则表达式和一个字符串,并以 Match 对象的形式返回匹配的第一个元素。如果没有匹配到,则会返回 None。(关于 search 函数先了解这些就可以,后面会有详细讲解。)

下面就拿这个示例中 re.search 中的参数来匹配下上面的概念,加深一下理解

  • 'wo\w+d' 就是正则表达式,它还有一个名称叫做_模式(pattern)_ ,表示wo 字母后有多个字母并一直到d 字母出现为止(现在不明白没关系,只要知道它就是正则表达式就可以了,后面会详细讲
  • 'wo\w+d' 前面还有一个 r 表示什么呢?这个 r 表示 raw的意思,就是原始字符串。原始字符串不会将 \ 解释成一个转义字符,而是这样做对正则表达式好处是大大的,只有这样 \w 才能起作用。
  • 'hello world!' 就是要匹配的字符串。
  • 整个函数就表示从 'hello world!' 字符串中搜索出符合_'wo\w+d'_ 模式的字符串,并展示出来,于是 world 字符串就被筛选了出来。

正则表达式有什么用?

我们学习正则表达式的目的是什么?当然是为了有朝一日能使用它解决我们面临的问题,要不然,学它干嘛。那我们就来聊聊正则表达式的用途:

  • 字符串验证 你肯定在网页上注册过账号吧,假如你在注册 github 网站,它要求你输入 Email,而你却胡乱填写了几个数字就想注册,这时就会弹出提示 "Email is invalid",意思就是你的邮箱是无效的,这就是正则表达式的功劳。
  • 替换文本 假如你正在写一篇关于 java 的文章,写着写着,你觉得换成 python 更好些,你想一下把出现 java , Java 的地方全都替换成 python , 正则表达式可以帮你做到。
  • 从字符串中提取出要获取的字符串 假如你正在爬取一个汽车排行榜页面,想要获取每个车型的编号,而车型编号则隐藏在链接中,怎么获取呢?用正则表达式可以。

正则表达式的语法及使用实例

对刚接触的同学来说,正则表达式的语法很晦涩。不用担心,我们先大致浏览一下完整的语法组合,后面再使用前面讲过的 re.search 方法一个个详细介绍,讲着讲着,我相信你就明白了。

正则表达式语法有哪些?

字符

功能描述

\

特殊字符转义

^

匹配字符串的开始位置

$

匹配字符串的结束位置

*

匹配前面的子表达式零次或多次

+

匹配前面的子表达式一次或多次

?

匹配前面的子表达式零次或一次

{n}

n是非负整数,匹配n次

{n,}

n 是非负整数,匹配 >=n 次

{n,m}

m是非负整数,n<=m, 匹配>= n 并且 <=m 次

?

非贪心量化

.

匹配除“\r”“\n”之外的任何单个字符

(pattern)

匹配pattern并获取这一匹配的子字符串

(?:pattern)

非获取匹配

(?=pattern)

正向肯定预查

(?!pattern)

正向否定预查

(?<=pattern)

反向(look behind)肯定预查

(?<!pattern)

反向否定预查

x|y

没有包围在()里,其范围是整个正则表达式

[xyz]

字符集合(character class),匹配所包含的任意一个字符

[^xyz]

排除型字符集合(negated character classes),匹配未列出的任意字符

[a-z]

字符范围,匹配指定范围内的任意字符

[^a-z]

排除型的字符范围,匹配任何不在指定范围内的任意字符

\b

匹配一个单词边界,也就是指单词和空格间的位置

\B

匹配非单词边界

\cx

匹配由x指明的控制字符

\d

匹配一个数字字符。等价于[0-9]

\D

匹配一个非数字字符。等价于[^0-9]。

\f

匹配一个换页符。等价于\x0c和\cL。

\n

匹配一个换行符。等价于\x0a和\cJ。

\r

匹配一个回车符。等价于\x0d和\cM。

\s

匹配任何空白字符。等价于[ \f\n\r\t\v]

\S

匹配任何非空白字符。等价于[^ \f\n\r\t\v]。

\t

匹配一个制表符。等价于\x09和\cI。

\v

匹配一个垂直制表符。等价于\x0b和\cK。

\w

匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”

\W

匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。

\ck

匹配控制转义字符。k代表一个字符。等价于“Ctrl-k”

\xnn

十六进制转义字符序列

\n

标识一个八进制转义值或一个向后引用

\un

Unicode转义字符序列

这些正则到底该怎么用?

浏览一遍,感觉怎么样,是不是摩拳擦掌,想要立刻实践一番,嘿嘿。好的我们现在就开干。

  • ^ 匹配字符串的开始位置 >>> import re >>> re.search(r'^h', 'he is a hero!') <re.Match object; span=(0, 1), match='h'> 这个例子中虽然有两个 h,因为前面有 ^ 所以只会匹配第一个
  • $ 匹配字符串的结束位置 >>> import re >>> re.search(r't$','this is an object') <re.Match object; span=(16, 17), match='t'> 虽然这个句子前后都有 t,却是最后的被匹配到了
  • * 匹配前面的子表达式 0 次或多次,例如,"zo*" 能匹配"z","zo","zoo",我们来验证下 >>> import re >>> re.search(r'zo*', 'z') <re.Match object; span=(0, 1), match='z'> >>> re.search(r'zo*', 'zo') <re.Match object; span=(0, 2), match='zo'> >>> re.search(r'zo*', 'zoo') <re.Match object; span=(0, 3), match='zoo'> ​ 这里 * 还有一种写法 {0,},两者等价。其中 {} 叫做重复。来看例子。 import re re.search(r'zo{0,}','z') <_sre.SRE_Match object; span=(0, 1), match='z'> re.search(r'zo{0,}','zo') <_sre.SRE_Match object; span=(0, 2), match='zo'> re.search(r'zo{0,}','zoo') <_sre.SRE_Match object; span=(0, 3), match='zoo'>
  • + 匹配前面的子表达式一次或多次,以 "zo+" 为例,它能匹配 "zo","zoo",来验证下 >>> import re >>> re.search(r'zo+', 'zo') <re.Match object; span=(0, 2), match='zo'> >>> re.search(r'zo+', 'zoo') <re.Match object; span=(0, 3), match='zoo'> 这里 + 还有一种写法 {1,} 两者是等价的,来看例子。 >>> import re >>> re.search(r'zo{1,}','zo') <re.Match object; span=(0, 2), match='zo'> >>> re.search(r'zo{1,}','zoo') <re.Match object; span=(0, 3), match='zoo'>
  • ? 匹配前面的子表达式0次或 1次,以 "ab(cd)?" 为例,可以匹配 "ab","abcd",看下面例子 import re re.search(r'ab(cd)?','ab') <_sre.SRE_Match object; span=(0, 2), match='ab'> re.search(r'ab(cd)?','abcd') <_sre.SRE_Match object; span=(0, 4), match='abcd'> 这里 ? 还有一种写法 {0,1} 两者等价,看下面 import re re.search(r'ab(cd){0,1}', 'ab') <_sre.SRE_Match object; span=(0, 2), match='ab'> re.search(r'ab(cd){0,1}', 'abcd') <_sre.SRE_Match object; span=(0, 4), match='abcd'>
  • {n} n 必须是非负整数,能匹配确定的 n 次,以 "o{2}" 为例,它能匹配 "good", 却不能匹配 "god" import re re.search(r'o{2}', 'god') re.search(r'o{2}', 'good') <_sre.SRE_Match object; span=(1, 3), match='oo'>
  • {n,} n是一个非负整数。至少能匹配 n次。例如,“o{2,}”不能匹配 “god”中的 “o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”,这个可看前面示例。
  • {n,m} m和n均为非负整数,其中n<=m。例如,“o{1,2}”将匹配“google”中的两个o。“o{0,1}”等价于“o?”。注意在逗号和两个数之间不能有空格 re.search(r'o{1,2}', 'google') <_sre.SRE_Match object; span=(1, 3), match='oo'>
  • ? 这个叫做非贪心量化(Non-greedy quantifiers),这个字符和前面的 ? 有什么区别?应用场合是什么呢? 当该字符紧跟在任何一个其他重复修饰符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。举个例子,"o+" 默认会匹配 o 一次或多次,如果在后面加上 "?",则匹配一次。来看代码。 re.search(r'o+?', 'google') <_sre.SRE_Match object; span=(1, 2), match='o'> re.search(r'o+', 'google') <_sre.SRE_Match object; span=(1, 3), match='oo'>
  • . 匹配除了 \r ,\n 之外的任何单个字符,要匹配包括“\r”“\n”在内的任何字符,请使用像“(.|\r|\n)”的模式 import re re.search(r'a.', 'ab') <_sre.SRE_Match object; span=(0, 2), match='ab'>
  • (pattern) 匹配 pattern 并获取这一匹配的子字符串,并用于向后引用。使用圆括号可以指定分组。当使用分组时,除了获取到整个匹配的完整字符串,也可以从匹配中选择每个单独的分组。 下面给出一个本地电话号码的示例,其中每个括号内匹配的数字都是一个分组。 >>> import re >>> match = re.search(r'([\d]{3,4})-([\d]{7,8})', '010-12345678') >>> match <re.Match object; span=(0, 12), match='010-12345678'> >>> match.group(1) '010' >>> match.group(2) '12345678' >>> match.group() '010-12345678' >>> match.groups() ('010', '12345678') 前面我们只是简单介绍了 match 对象,为了深入的理解分组,这里还要简单介绍下该对象的几个方法以及如何对应分组信息的:
    • groups() 用于返回一个对应每一个单个分组的元组。 >>> match.groups() ('010', '12345678')
    • group() 方法(不含参数)则返回完整的匹配字符串 >>> match.group() '010-12345678'
    • group(num) num 是分组编号,按照分组顺序,从 1 开始取值,能获取具体的分组数据。 >>> match.group(1) '010' >>> match.group(2) '12345678'
  • (?:pattern) 匹配 pattern 但不获取匹配的子字符串(shy groups),也就是说这是一个非获取匹配,不存储匹配的子字符串用于向后引用。这种格式的圆括号不会作为分组信息,只用于匹配,即在python 调用search 方法而得到的 match 对象不会将圆括号作为分组存储起来。 来看下面例子,只获取电话号,而不获取地区区号。 >>> match = re.search(r'(?:[\d]{3,4})-([\d]{7,8})', '010-12345678') >>> match.groups() ('12345678',) >>> match.group() '010-12345678' 这种形式对使用 字符`(|)”来组合一个模式的各个部分是很有用的,来看一个例子,想要同时匹配 city 和 cities (复数形式),就可以这样干 >>> match = re.search(r'cit(?:y|ies)','cities') >>> match <re.Match object; span=(0, 6), match='cities'> >>> match = re.search(r'cit(?:y|ies)','city') >>> match <re.Match object; span=(0, 4), match='city'>
  • (?=pattern)正向肯定预查(look ahead positive assert),在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。 举个例子,假设我要获取从不同 python 版本中只获取 "python" 字符串,就可以这样写: >>> match = re.search(r'python(?=2.7|3.5|3.6|3.7)', 'python3.7') >>> match <re.Match object; span=(0, 6), match='python'> 预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。那么在 python 版本后再加上其他信息,整体就无法匹配了。 看下面例子,得到的结果只能是 null。 >>> match = re.search(r'python(?=2.7|3.5|3.6|3.7) is hacking!', 'python3.7 is hacking!') >>> match
  • (?!pattern) 正向否定预查(negative assert),看名字也知道是 正向肯定预查的反面。在任何不匹配 pattern 的字符串开始处匹配查找字符串。是一个非获取匹配,而且预查不消耗字符。 看下面例子,和正向肯定预查一对比就明白了。 >>> match = re.search(r'python(?!2.7|3.5|3.6|3.7)', 'python3.7') >>> match >>> match = re.search(r'python(?!2.7|3.5|3.6|3.7)', 'python3.1') >>> match <re.Match object; span=(0, 6), match='python'> >>> match = re.search(r'python(?!2.7|3.5|3.6|3.7) is hacking!', 'python3.1 is hacking!') >>> match
  • (?<=pattern) 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。
  • (?<!pattern) 反向否定预查,与正向否定预查类似,只是方向相反。
  • x|y 或,分两种情况:没有没括号包围,范围则是整个表达式;被括号包围,返回是括号内。 >>> match = re.search(r'today is sunday|tommory is monday','tommory is monday') >>> match <re.Match object; span=(0, 17), match='tommory is monday'>
  • [xyz] 字符集合,匹配所包含的任意一个字符。分为下面情况
    • 普通字符 >>> match = re.search(r'[Pp]ython','python') >>> match <_sre.SRE_Match object; span=(0, 6), match='python'>
    • 特殊字符仅有反斜线 \保持特殊含义,用于转义字符
    • 其它特殊字符如星号、加号、各种括号等均作为普通字符 >>> match = re.search(r'[*?+()]python','*python') >>> match <_sre.SRE_Match object; span=(0, 7), match='*python'> >>> match = re.search(r'[*?+()]python','+python') >>> match <_sre.SRE_Match object; span=(0, 7), match='+python'> >>> match = re.search(r'[*?+()]python','(python') >>> match <_sre.SRE_Match object; span=(0, 7), match='(python'>
    • ^ 出现在字符串中间和末尾仅作为普通字符,出现在最前面后面会讲。 >>> match = re.search(r'[*^{]python','^python') >>> match <_sre.SRE_Match object; span=(0, 7), match='^python'> >>> match = re.search(r'[*^]python','^python') >>> match <_sre.SRE_Match object; span=(0, 7), match='^python'>
    • - 出现在字符集合首位和末尾,仅作为普通字符,出现在中间是字符范围描述,后面会讲。 >>> match = re.search(r'[-^]python','-python') >>> match <_sre.SRE_Match object; span=(0, 7), match='-python'>
  • [^xyz] 排除型字符集合(negated character classes)。匹配未列出的任意字符 >>> re.search(r'[^abc]def','edef') <re.Match object; span=(0, 4), match='edef'>
  • [a-z] 字符范围。匹配指定范围内的任意字符。 >>> re.search(r'[a-g]bcd','ebcd') <re.Match object; span=(0, 4), match='ebcd'> >>> re.search(r'[a-g]bcd','hbcd')
  • [^a-z] 排除型的字符范围。匹配任何不在指定范围内的任意字符。 >>> re.search(r'[^a-g]bcd','hbcd') <re.Match object; span=(0, 4), match='hbcd'>
  • \b 匹配一个单词边界,也就是指单词和空格间的位置。 >>> re.search(r'an\b apple','an apple') <re.Match object; span=(0, 8), match='an apple'>
  • \B 匹配非单词边界 >>> re.search(r'er\B','verb') <re.Match object; span=(1, 3), match='er'>
  • \d 匹配一个数字字符。等价于[0-9] >>> re.search(r'\d apples', '3 apples') <re.Match object; span=(0, 8), match='3 apples'>
  • \D 匹配一个非数字字符。等价于[^0-9] >>> re.search(r'\D dog', 'a dog') <re.Match object; span=(0, 5), match='a dog'>
  • \s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。 >>> re.search(r'a\sdog', 'a dog') <re.Match object; span=(0, 5), match='a dog'>
  • \S 匹配任何非空白字符。等价于[^ \f\n\r\t\v] >>> re.search(r'\S dog', 'a dog') <re.Match object; span=(0, 5), match='a dog'>
  • \w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]” >>> re.search(r'\w', 'h') <re.Match object; span=(0, 1), match='h'>
  • \W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]” >>> re.search(r'\W', '@') <re.Match object; span=(0, 1), match='@'>
  • \un Unicode转义字符序列。其中n是一个用四个十六进制数字表示的Unicode字符 >>> re.search(r'\u00A9','©') <re.Match object; span=(0, 1), match='©'>

小结

如果你真的读完了这些实例,我敢说你对正则表达式会有一定的理解了吧。下篇会重点讲解python 中的正则表达式库函数,对中文的处理等,敬请期待~

参考文档

  1. 维基百科—正则表达式

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python中的正则表达式

    句点(.)符号匹配除了换行符\n以外的任何字符。无论字母、数字、空格(不包括\n换行符)、可打印字符、不可打印字符,使用.都可以匹配。

    py3study
  • 同样是Python,Python3和P

    上周,我的测试同事告诉我,你的用户名怎么还允许中文啊?当时我心里就想,你们测试肯定又搞错接口了,我用的是正则w过滤了参数,怎么可能出错,除非Python正则系统...

    py3study
  • 第一章:正则表达式

      正则表达式是由一堆字符和特殊符号组成的字符串。它可以为我们提供高级的文本搜索,匹配,替换功能。当然,正则表达式也不是python独有的一种模式,而是凌驾于语...

    py3study
  • Python中的正则表达式

    句点(.)符号匹配除了换行符\n以外的任何字符。无论字母、数字、空格(不包括\n换行符)、可打印字符、不可打印字符,使用.都可以匹配。

    py3study
  • Python学习(二) 正则表达式

    Python正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re 模块使 Python 语言拥有全部的正则表达式功...

    深度学习思考者
  • python基础-正则使用

    re.match尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回None

    dogfei
  • python爬虫笔记之re.match匹配,与search、findall区别

    网上的定义【 从要匹配的字符串的头部开始,当匹配到string的尾部还没有匹配结束时,返回None;  当匹配过程中出现了无法匹配的字母,返回None。】 

    逆向小白
  • 第一章:正则表达式

      正则表达式是由一堆字符和特殊符号组成的字符串。它可以为我们提供高级的文本搜索,匹配,替换功能。当然,正则表达式也不是python独有的一种模式,而是凌驾于语...

    py3study
  • 还不会Python正则表达式?看这篇文章试试

    今天写爬虫偶然想到了初学正则表达式时候,看过一篇文章非常不错。检索一下还真的找到了。 re模块 re.search 经常用match = re.search(p...

    小小科
  • 简单的正则表达式

    特殊字符 ^ $ * ? + {2} {2,} {2,5} | [] [^] [a-z] . \s \S \w \W [\u4E00-\u9FA5] ...

    听城

扫码关注云+社区

领取腾讯云代金券