<返回更多

适用于初学者学习的Python正则表达式

2020-07-14    
加入收藏

“代码胜于雄辩。”

——林纳斯·托瓦兹(Linus Torvalds)

许多编程语言和操作系统都支持正则表达式(regular expression):定义搜索模式的一组字符串。正则表达式可用于检索文件或其他数据中是否存在指定的复杂模式。例如,可使用正则表达式匹配文件中所有的数字。本章将学习如何定义正则表达式,将其传入类UNIX操作系统以用来检索文件的grep命令。该命令会返回文件中与指定模式匹配的文本。我们还将学习在Python中使用正则表达式检索字符串。

17.1 初始配置

开始之前,先创建一个名为zen.txt的文件。在命令行中(确保位于zen.txt所在的目录)输入命令python3 -c "import this",这会打印出蒂姆·皮特斯(Tim Peters)写的诗The Zen of Python(Python之禅):

Python之禅

 

优美胜于丑陋

明了胜于晦涩

简洁胜于复杂

复杂胜于凌乱

扁平胜于嵌套

间隔胜于紧凑

可读性很重要

即便假借特例的实用性之名,也不可违背这些规则

不要包容所有错误,除非你确定需要这样做

当存在多种可能,不要尝试去猜测

而是尽量找一种,最好是唯一一种明显的解决方案

虽然这并不容易,因为你不是Python之父

做也许好过不做,但不假思索就动手还不如不做

如果你无法向人描述你的方案,那肯定不是一个好方案

命名空间是一种绝妙的理念,我们应当多加利用

旗标-c告诉Python传入的字符串中包含有Python代码。然后Python会执行传入的代码。Python执行import this之后,将打印The Zen of Python(像上述这首诗一样隐藏在代码中的信息,也被称为彩蛋)。在Bash中输入exit()函数退出Python,然后将诗的内容复制到文件zen.txt中。

在Ubuntu系统中,grep命令默认在输出时以红色字体打印匹配的文本,但是在UNIX系统中则不是这么做的。如果使用的是mac,可以通过在Bash中修改如下环境变量来改变该行为:

# http://tinyurl.com/z9prphe

$ export GREP_OPTIONS='--color=always'
$ export GREP_OPTIONS='--color=always'

要记住,在Bash中直接设置环境变量的方式是不持久的,如果退出Bash,下次再打开时必须重新设置环境变量。因此,可将环境变量添加至.profile文件,使其持久存在。

17.2 简单匹配

grep命令接受两个参数:一个正则表达式和检索正则表达式中定义模式的文件路径。使用正则表达式进行最简单的模式匹配,就是简单匹配,即一个字符串匹配单词中相同的字符串。举个例子,在zen.txt文件所在的目录输入如下命令:

# http://tinyurl.com/jgh3x4c

$ grep Beautiful zen.txt

>> Beautiful is better than ugly.

上例中执行的命令里,第一个参数Beautiful是一个正则表达式,第二个参数zen.txt是检索正则表达式的文件。Bash打印了Beautiful is better than ugly.这句话,其中Beautiful为红色,因为它是正则表达式匹配上的单词。

如果将上例中的正则表达式从Beautiful修改为beautiful,grep将无法匹配成功:

# http://tinyurl.com/j2z6t2r

$ grep beautiful zen.txt

当然,可以加上旗标-i来忽略大小写:

# http://tinyurl.com/zchmrdq

$ grep -i beautiful zen.txt

>> Beautiful is better than ugly.

grep命令默认打印匹配文本所在的整行内容。可以添加旗标-o,确保只打印与传入的模式参数相匹配的文本:

# http://tinyurl.com/zfcdnmx

$ grep -o Beautiful zen.txt

>> Beautiful

也可通过内置模块re在Python中使用正则表达式。re模块提供了一个叫findall的方法,将正则表达式和目标文本作为参数传入,该方法将以列表形式返回文本中与正则表达式匹配的所有元素:

01  # http://tinyurl.com/z9q2286
02  
03  
04  <strong>import</strong> re
05  
06  
07  l = "Beautiful is better than ugly."
08  
09  
10  matches = re.findall("Beautiful", l)
11  
12  
13  print(matches)

>> ['Beautiful']

本例中findall方法只找到了一处匹配,返回了一个包含匹配结果[Beautiful]的列表。

将re.IGNORECASE作为第3个参数传入findall,可以让其忽略大小写:

01  # http://tinyurl.com/jzeonne
02  
03  
04  <strong>import</strong> re
05  
06  
07  l = "Beautiful is better than ugly."
08  
09  
10  matches = re.findall("beautiful", 
11                        l, 
12                        re.IGNORECASE)
13  
14  
15  print(matches)

>> ['Beautiful']

17.3 匹配起始位置

我们还可以在正则表达式中加入特殊字符来匹配复杂模式,特殊字符并不匹配单个字符,而是定义一条规则。例如,可使用补字符号 ^ 创建一个正则表达式,表示只有模式位于行的起始位置时才匹配成功:

# http://tinyurl.com/gleyzan

$ grep ^If zen.txt

>> If the implementation is hard to explain, it is a bad idea.
>> If the implementation is easy to explain, it may be a good idea.

类似地,还可使用美元符号$来匹配结尾指定模式的文本行:

# http://tinyurl.com/zkvpc2r

$ grep idea.$ zen.txt

>> If the implementation is hard to explain, it is a bad idea.
>> If the implementation is easy to explain, it may be a good idea.

本例中,grep忽略了Namespaces are one honking great idea -- let us do more of those!这行,因为它虽然包含了单词idea,但并不是以其结尾。

下例是在Python中使用补字符 ^ 的示例(必须传入re.MULITILINE作为findall的第3个参数,才能在多行文本中找到所有匹配的内容):

01  # http://tinyurl.com/zntqzc9
02  
03  
04  <strong>import</strong> re
05  
06  
07  zen = """Although never is
08  often better than
09  *right* now.
10  If the implementation
11  is hard to explain,
12  it's a bad idea.
13  If the implementation
14  is easy to explain,
15  it may be a good
16  idea. Namesapces
17  are one honking
18  great idea -- let's
19  do more of those!
20  """
21  
22  
23  m = re.findall("^If",
24                 zen,
25                 re.MULITILINE)
26  print(m)

>> ['If', 'If']

17.4 匹配多个字符

将正则表达式的多个字符放在方括号中,即可定义一个匹配多个字符的模式。如果在正则表达式中加入[abc],则可匹配a、b或c。在下一个示例中,我们不再是直接匹配zen.txt中的文本,而是将字符串以管道形式传给grep进行匹配。示例如下:

# http://tinyurl.com/jf9qzuz

$ echo Two too. <strong>|</strong> grep -i t[ow]o

>> Two too

echo命令的输出被作为输入传给grep,因此不用再为grep指定文件参数。上述命令将two和too都打印出来,是因为正则表达式均匹配成功:第一个字符为t,中间为o或w,最后是o。

Python实现如下:

01  # http://tinyurl.com/hg9sw3u
02  
03  
04  <strong>import</strong> re
05  
06  
07  string = "Two too."
08  
09  
10  m = re.findall("t[ow]o",
11                 string,
12                 re.IGNORECASE)
13  print(m)

>> ['Two', 'too']

17.5 匹配数字

可使用[[:digit:]]匹配字符串中的数字:

# http://tinyurl.com/gm8o6gb

$ echo 123 hi 34 hello. <strong>|</strong> grep [[:digit:]]

>> 123 hi 34 hello.

在Python 中使用d匹配数字:

1  # http://tinyurl.com/z3hr4q8
2  
3  
04  <strong>import</strong> re
05  
06  
07  line = "123?34 hello?"
08  
09  
10  m = re.findall("d",
11                 line,
12                 re.IGNORECASE)
13  
14  
15  print(m)

>> ['1', '2', '3', '3', '4']

17.6 重复

星号符*可让正则表达式支持匹配重复字符。加上星号符之后,星号前面的元素可匹配零或多次。例如,可使用星号匹配后面接任意个o的tw:

# http://tinyurl.com/j8vbwq8

$ echo two twoo not too. <strong>|</strong> grep -o two*

>> two
>> twoo

在正则表达式中,句号可匹配任意字符。如果在句号后加一个星号,这将让正则表达式匹配任意字符零或多次。也可使用句号加星号,来匹配两个字符之间的所有内容:

# http://tinyurl.com/h5x6cal

$ echo __hello__there <strong>|</strong> grep -o __.*__

>> __hello__

正则表达式__.*__可匹配两个下划线之间(包括下划线)的所有内容。星号是贪婪匹配(greedy),意味着会尽可能多地匹配文本。例如,如果在双下划线之间加上更多的单词,上例中的正则表达式也会匹配从第一个下划线到最后一个下划线之间的所有内容:

# http://tinyurl.com/j9v9t24

$ echo __hi__bye__hi__there <strong>|</strong> grep -o __.*__

>> __hi__bye__hi__

如果不想一直贪婪匹配,可以在星号后面加个问号,使得正则表达式变成非贪婪模式(non-greedy)。一个非贪婪的正则表达式会尽可能少地进行匹配。在本例中,将会在碰到第一个双下线后就结束匹配,而不是匹配第一个和最后一个下划线之间的所有内容。grep并不支持非贪婪匹配,但是在Python中可以实现:

01  # http://tinyurl.com/j399sq9
02  
03  
04  <strong>import</strong> re
05  
06  
07  t = "__one__ __two__ __three__"
08  
09  
10  found = re.findall("__.*?__", t)
11  
12  
13  <strong>for</strong> match <strong>in</strong> found:
14      print(match)

>> __one__
>> __two__
>> __three__

我们可通过Python中的非贪婪匹配,来实现游戏Mad Libs(本游戏中会给出一段文本,其中有多个单词丢失,需要玩家来补全):

01  # http://tinyurl.com/ze6oyua
02  
03  <strong>import</strong> re
04  
05  
06  text = """Giraffes have aroused
07   the curIOSity of __PLURAL_NOUN__
08   since earliest times. The
09   giraffe is the tallest of all
10   living __PLURAL_NOUN__, but
11   scientists are unable to
12   explain how it got its long
13   __PART_OF_THE_BODY__. The
14   giraffe's tremendous height,
15   which might reach __NUMBER__
16   __PLURAL_NOUN__, comes from
17   it legs and __BODYPART__.
18  """
19  
20  
21  <strong>def</strong> mad_libs(mls):
22      """
23      :param mls:字符串
24      双下划线部分的内容要由玩家来补充。
25      双下划线不能出现在提示语中,如不能
26      出现 __hint_hint__,只能是 __hint__。
27  
28  
29  
30  
31      """
32      hints = re.findall("__.*?__",
33                         mls)
34      <strong>if</strong> hints <strong>is</strong> <strong>not</strong> None:
35          <strong>for</strong> word <strong>in</strong> hints:
36              q = "Enter a {}".format(word)
37              new = input(q)
38              mls = mls.replace(word, new, 1)
39          <strong>print</strong>("n")
40          mls = mls.replace("n", "")
41          print(mls)
42      <strong>else</strong>:
43          <strong>print</strong>("invalid mls")
44  
45  
46  mad_libs(text)

>> enter a __PLURAL_NOUN__

本例中,我们使用re.findall匹配变量text中所有被双下划线包围的内容(每个均为玩家需要输入答案进行替代的内容),以列表形式返回。然后,对列表中的元素进行循环,通过每个提示来要求玩家提供一个新的单词。之后,创建一个新的字符串,将提示替换为玩家输入的词。循环结束后,打印替换完成后的新字符串。

17.7 转义

我们可以在正则表达式中对字符进行转义(忽略字符的意义,直接进行匹配)。在正则表达式中的字符前加上一个反斜杠即可进行转义:

# http://tinyurl.com/zkbumfj

$ echo I love $ <strong>|</strong> grep \$

>> I love $

通常情况下,美元符号的意思是出现在匹配文本行尾时才有效,但是由于我们进行了转义,这个正则表达式只是匹配目标文本中的美元符号。

Python实现如下:

01  # http://tinyurl.com/zy7pr41
02  
03  
04  <strong>import</strong> re
05  
06  
07  line = "I love $"
08  
09  
10  m = re.findall("\$",
11                 line,
12                 re.IGNORECASE)
13  
14  
15  print(m)

>> ['$']

17.8 正则工具

找到匹配模式的正则表达式是一件很困难的事。可前往http://theselftaughtprogrammer. io/regex了解有助于创建正则表达式的工具。

17.9 术语表

正则表达式:定义检索模式的字符串序列。

菜单:代码中隐藏的信息。

贪婪匹配:尽量多地匹配文本的正则表达式。

非贪婪匹配:尽可能少地进行文本匹配的正则表达式。

本文摘自《Python编程无师自通——专业程序员的养成》

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>