Python 入门编程课系列:Python正则表达式:处理文本的利器

作者:禅与计算机程序设计艺术

1.背景介绍

今天我们将学习Python中的正则表达式。正则表达式(Regular Expression)在计算机领域中是一个重要的工具,它可以帮助你快速、准确地查找、替换文字或者字符串中的特定模式。本文涵盖的内容主要是从零开始,带着大家进入正则表达式的世界。如果你已经了解了基本的Python语法和数据类型,那么本文应该能够帮助你深入理解正则表达式。

2.核心概念与联系

正则表达式(Regular Expression),也称为正规表示法,是一种特殊的字符序列,它描述了一条或多条匹配的规则。它的功能类似于一个常规搜索引擎,但其搜索规则比一般的搜索更为精确,能更好地控制和取代文本内容。Python中的re模块提供了对正则表达式进行处理的函数。

Python中的正则表达式语法非常简单,总体来说分为三个部分:

  1. ^ : 表示行的开头。
  2. $: 表示行的结尾。
  3. .: 表示任意单个字符(除了换行符)。

举例:

import re

pattern = "^hello"
string = "hello world!"

match_obj = re.search(pattern, string)
if match_obj:
    print("Match found at index", match_obj.start())
else:
    print("No match")

以上代码可以用来判断字符串是否以“hello”开头,如果字符串中含有“hello”,则会输出其位置索引值。

再如,以下代码用于查找一个数字串中连续出现的3个数字。

import re

pattern = r"\d{3}"
string = "The number is 12345."

matches = re.findall(pattern, string)
print(matches)

以上代码可以输出所有连续出现的3个数字:[‘123’, ‘45’]。

当然,正则表达式还有很多种用法,本文所涉及到的只是很小的一部分。

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

3.1 概念

3.1.1 什么是正则表达式

正则表达式,又称规则表达式,是一种文本模式,用来匹配字符串中符合某些规则的子串。常见的应用场景包括验证邮箱地址、URL、电话号码等;字符串截取、替换、检索、排序、过滤等等。

3.1.2 为什么要用正则表达式

正则表达式的优点在哪里?为什么大家都喜欢用正则表达式?

首先,正则表达式的速度非常快,对于一些复杂的字符串匹配,相比传统的循环匹配,它的速度可是提升了不少。其次,正则表达式能有效地简化代码,避免了很多无聊且重复的工作。再次,正则表达式有着丰富的功能和强大的自定义能力,可以匹配各种各样的字符串模式。最后,正则表达式有着完善的文档支持,几乎任何开发者都会喜欢上它。

3.1.3 如何使用正则表达式

先举个简单的例子,看一下如何使用正则表达式去匹配一个邮箱地址:

import re

email = ""

match_result = re.match("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", email)
if match_result:
  print('Valid Email')
else:
  print('Invalid Email')

上面的代码将匹配以字母、数字、下划线、百分号、加号、句点组成的邮箱地址。具体流程如下:

首先导入re模块,然后创建待匹配的字符串email。接着,使用re.match()方法对email进行匹配。这里传入的参数是正则表达式,它指定了邮箱地址的规则。这个正则表达式如下:

^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$

那该怎么理解这个正则表达式呢?

  • ^ : 表示行的开头。
  • [a-zA-Z0-9._%+-]+ : 匹配至少一个字母、数字、下划线、百分号、加号的组合,即用户名。
  • @ : 表示邮箱的隔离符号。
  • [a-zA-Z0-9.-]+ : 匹配至少一个字母、数字、下划线、句点的组合,即域名。
  • \. : 表示句点的转义字符。
  • [a-zA-Z]{2,} : 匹配至少两个英文字母的组合,作为顶级域名。
  • $ : 表示行的结尾。

用途很广,比如密码校验、网页爬虫等等。

还可以进一步拓展这个正则表达式:

import re

emails = ['', '[email protected]','invalid-email!example.com']

for email in emails:

  # 使用re.match()方法对email进行匹配
  match_result = re.match("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", email)
  
  if not match_result:
      print('%s is Invalid' % email)  
      
  else: 
      print('%s is Valid'% email)  

其中,emails是一个列表,包含多个邮箱地址。使用for循环遍历列表,对每个邮箱地址调用re.match()方法进行匹配。若成功匹配,则打印“Valid”,否则打印“Invalid”。

其他的一些匹配规则,比如手机号码匹配、身份证号码匹配等,也是可以通过正则表达式完成。

3.2 模式

3.2.1 模式的定义

模式(Pattern)是正则表达式的基础,它是由普通字符(例如:字母,数字和符号)以及特殊字符(称为元字符)组成的文字模板。这些模板可以让你方便地表示对字符串的各种匹配或替代操作。

正则表达式是一个高度优化的匹配算法,通过设置一些规则来描述字符序列的模式。当搜索模式与输入字符串相匹配时,正则表达式引擎便返回一个匹配结果。

我们用一个示例来理解模式的概念。假设有一个字符串"Hello World!“,希望匹配出第一个"World”。

  • 如果模式是“Worl”,则匹配不到,因为"Worl"只有两个字符,但是字符串中存在四个字符。
  • 如果模式是“WoRl”,则匹配不到,因为中间的“r”不匹配。
  • 如果模式是“Wor?l”,则匹配不到,因为“l”后面不能跟非字母字符。
  • 如果模式是“Wor[ld]”,则只匹配到"World",因为它满足“[ld]”之一,所以它是一个字符集。

模式就是这样,它告诉正则表达式引擎,在某个位置应当匹配什么样的东西。

3.2.2 基本元素

模式的基本元素包括:字母(A-Za-z),数字(0-9),字符类(如:\w, \d,., |, [, ], ( ), { })以及空白字符(例如:空格、制表符等)。

3.2.3 边界匹配

边界匹配是在特定情况下使用的特殊字符。它们可以让你限定模式的匹配范围,有助于减少匹配的错误率。

^
  • 指示匹配必须从字符串的起始位置开始。
  • 例如:/^abc/ 只匹配字符串"abc"的开始。
  • 在模式中经常用作锚点,它可以让你对一组相关匹配进行更精确的定位。
$
  • 指示匹配必须到达字符串的结束位置。
  • 例如:"abc$"只匹配字符串"abc"的末尾。
\b
  • 指示匹配必须出现在词的边界。
  • 例如:/\bcat\b/ 匹配字符串"the cat in the hat" 中的"cat"。
\B
  • 指示匹配不能出现在词的边界。
  • 例如:/\Bc\Bat/ 匹配字符串"batman car batmobile" 中的"cat" 和 “bat” 。

3.2.4 替换

正则表达式也可以用来替换字符串中的内容,也就是说,你可以根据指定的模式替换掉原有的字符序列,从而实现一定的文本处理功能。

replace() 方法用于替换字符串中的匹配项。

例如:

import re

text = 'This is some text -- with punctuation.'

new_text = re.sub('\W+', '', text)    # 删除标点符号和空白字符
print(new_text)

输出:

Thiss is somt txwt - wth punctuatn.

另一个例子:

import re

text = '''Line 1
Line 2 has two spaces after it.'''

new_text = re.sub('\s+','', text).strip()     # 用单个空格替换多个空格并删除首尾空格
print(new_text)

输出:

Line 1 Line 2 has two spaces after it.

此外,还有很多其他的替换操作可以使用,比如删除换行符 \n,将字符串中所有的大写转换为小写 lower() ,将所有数字转换为常量 int(),甚至可以编写自己的函数来实现特定的替换逻辑。

3.2.5 预定义模式

正则表达式标准库提供了一些预定义的模式,可以用作匹配或替换的基本模式。它们包括:

  • \d 匹配任意十进制数字。
  • \D 匹配任意非十进制数字。
  • \s 匹配任意空白字符,包括空格、制表符、换行符。
  • \S 匹配任意非空白字符。
  • \w 匹配任意字母、数字和下划线。
  • \W 匹配任意非字母、数字和下划线。

3.3 字符类

字符类允许你匹配一组字符中的任何一个字符。

[ ]
  • 创建一个字符类。
  • 将括号中的任何一个字符放入方括号中即可创建一个字符类,例如:[aeiou] 可以匹配任何一个英文元音字母。
[^ ]
  • 创建否定字符类的排除模式。
  • 将反斜杠字符后面紧跟的字符放入方括号中,然后将其置于感叹号! 的左侧即可创建一个否定字符类。

####.

  • 匹配除换行符 (\n) 以外的所有字符。
  • 比如,[.com] 会匹配"google.com" 或 “website.cn”。
\w
  • 匹配任意字母数字或下划线字符。
  • 比如,\w+ 会匹配至少一个单词字符,即使中间夹杂了空格。
\d
  • 匹配任意十进制数字字符。
  • 比如,\d+ 会匹配至少一个数字字符。
\s
  • 匹配任意空白字符,包括空格、制表符、换行符。
  • 比如,\s+ 会匹配至少一个空白字符。
\b
  • 匹配单词的边界。
  • 当与 \w、\d 或 \s 一起使用时,\b 可以匹配单词的边界,而不会匹配到整个单词。

3.3.2 重复次数

除了字符类以外,模式还可以包含一些元字符来限定其出现次数。

*
  • 匹配前面的字符零次或多次。
  • 比如,ab*c 匹配 “ac”、“abc”、“abbbbc” 等。
+
  • 匹配前面的字符一次或多次。
  • 比如,ab+c 匹配 “abc”、“abbbbbc”。

####?

  • 匹配前面的字符零次或一次。
  • 比如,ab?c 匹配 “ac” 和 “abc”。
{m}
  • 指定匹配前面的字符出现 m 次。
  • 比如,ab{2}c 匹配 “abbccc”。
{m, n}
  • 指定匹配前面的字符出现 m~n 次。
  • 比如,ab{2,4}c 匹配 “abbccc”、“abbbcc” 和 “abbbbcccc”。
{m,}
  • 指定匹配前面的字符至少出现 m 次。
  • 比如,ab{2,}c 匹配 “abbbbbc”。

3.3.3 分支选择

模式还可以包含一些元字符来实现分支选择。

|
  • 创建分支选择,允许你尝试多种可能性。
  • 比如,x|y 可以匹配 “x” 或 “y”。
()
  • 创建一个子模式。
  • 子模式会被保存起来,并可以用于重复引用、分组或提取信息。
  • 比如,(apple|banana)\d+ 可以匹配 “apple2” 或 “banana42”。

3.3.4 后向引用

后向引用是一个比较复杂的特性,它允许你引用之前已匹配到的文本。

\number
  • 对先前的分组编号进行引用。
  • 比如,(\d{3})-(\d{3})-\d{4} 可以匹配 123-456-7890 或 987-654-3210。
\g
  • 通过名称对先前的分组进行引用。
  • 比如,(?P\d{3})-(?P\d{3})-(?P=area)(?P=prefix)-\d{4} 可以匹配 123-456-1234 或 987-654-9876。

3.4 贪婪与懒惰

正则表达式的默认行为是贪婪的。也就是说,正则表达式会尽可能多地匹配文本中的字符,直到遇到无法匹配的情况。这种做法可能会导致一些匹配上的困难,但它会让正则表达式更容易使用。

当你想要编写一个更灵活的匹配方案时,可以添加限定符来改变这种默认行为。

####?

  • 从左往右扫描文本,尽可能少地匹配。
  • 例如,ab?c 会匹配 “ac” 或 “abc”。

####??

  • 不匹配字符或字符类两边的字符。
  • 例如,(?:ab)?c 会匹配 “ac” 或 “abc”,但不会匹配 “abbc”。
*?
  • 从左往右扫描文本,尽可能少地匹配。
  • 例如,ab*c 会匹配 “acc” 或 “abbbbc”。
+?
  • 从左往右扫描文本,尽可能少地匹配。
  • 例如,ab+c 会匹配 “abc” 或 “abbbbbc”。
{m}?
  • 指定最短匹配数量。
  • 比如,ab{2}?c 会匹配 “ac” 或 “abc”。
{m,}?
  • 指定最少匹配数量。
  • 比如,ab{2,}?c 会匹配 “ac” 或 “abc”。
{m,n}?
  • 指定匹配范围,尽可能少地匹配。
  • 比如,ab{2,4}?c 会匹配 “ac” 或 “abc” 或 “abbbbc” 或 “abbbbbbc”。
()?
  • 可选捕获。
  • 捕获的内容可有可无。
  • 例如,(abc)? 会匹配 “abc” 或 “”。

你可能感兴趣的:(Python入门实战,Python,Java,React)