当前位置: 首页 > news >正文

技能补全之正则表达式

正则表达式

  • 〇、前言
  • 一、基本概念
    • 1、基本运算单元
    • 2、元字符
      • 2.1、量词
      • 2.2、字符集
      • 2.3、边界匹配
      • 2.4、分组
      • 2.5、特殊字符
      • 2.6、实践案例
      • 3、标志
    • 二、运算优先级

〇、前言

对于程序员职业,无论是全职的开发,还是自动化测试,都少不了进行文本校验操作。而诸多文本校验操作,有笨拙的,也有高效的。高效的文本校验操作,少不了使用 正则表达式,因为它本身就是根据字符串内容的组成规律而概括得来的模式

因为正则表达式的魅力,Python 内置相应能力进行支持,比如 re 模块,再比如 unittest 框架的 assertRegex 方法和 assertNotRegex 方法。下面,就结合 Python 的这两个模块,带领屏幕前的你,进行正则表达式的系统性学习。

一、基本概念

1、基本运算单元

模式,即pattern,是正则表达式运作的基本单元。模式串可简单也可复杂,简单的往往是不使用任何限制的字符串字面量,比如 『“once”』,就是一个简单的模式串,待校验的文本中只要包含同样内容,不论位置都算符合规则;复杂的模式串,往往会通过一个或多个元字符结合文本字符进行组装,比如『“^http|https”』可用来校验字符串内容是否以 http 或 https 开头。

在Python中,正则表达式使用字符串进行表达,为了与普通字符串进行区分,通常会在引号前使用 r 进行修饰:

pattern = r"^http|https"

此外,也可以使用 re 模块的 compile 方法去获得一个模式串,尤其是当模式串需要带上特定标志时。

2、元字符

清楚了基本概念之后,下面开始学习正则表达式语法中的元字符,所谓元字符,就是在整个正则表达式中起到特定功能的特定字符,可以理解为它们就是正则表达式中的预留关键字。

比较常用的元字符,主要有以下几类:

  • 量词
  • 字符集
  • 边界匹配
  • 分组
  • 特殊字符

2.1、量词

量词的功能在于校验次数,即预期字符出现的次数,目前一共有如下:

元字符功能意义
*匹配前面的模式零次或多次
+匹配前面的模式一次或多次
匹配前面的模式零次或一次
{n}匹配前面的模式恰好 n 次
{n,}匹配前面的模式至少 n 次
{n,m}匹配前面的模式至少 n 次且不超过 m 次

2.2、字符集

元字符功能
[]匹配括号内的任意一个字符
[^]匹配除了括号内的字符以外的任意一个字符

2.3、边界匹配

元字符功能
^匹配字符串开头
$匹配字符串结尾
\b匹配单词边界
\B匹配非单词边界

2.4、分组

元字符功能
()用于定义子表达式或捕获组
|表示『或』关系

2.5、特殊字符

特殊字符主要有 \.-,前者表示转义,用以匹配特殊字符本身;中者匹配除了换行符以外的任意字符,后者表示此到彼的意思,例如a-z就表示从a到z的所有字母。

2.6、实践案例

下面使用 unittest 框架对上述元字符进行逐一实践:

import re
import unittestclass MetaChar(unittest.TestCase):def test_matchAppearTimes(self):"""测试正则表达式语法中的量词:return:"""regex_endWithStar = r"^[a-zA-z0-9]*$"text = "aabbccddeefff1111223998"self.assertRegex(text, regex_endWithStar)regex_endWithAdd = r"^[a-zA-z]+$"self.assertNotRegex(text, regex_endWithAdd)text = "abcd"self.assertRegex(text, regex_endWithAdd)regex_endWithQuestion = r"^[A-z0-9]?$"self.assertNotRegex(text, regex_endWithQuestion)text = "A"self.assertRegex(text, regex_endWithQuestion)self.assertRegex("", regex_endWithQuestion)regex_A3Times = r"^A{3}"self.assertNotRegex(text, regex_A3Times)text = "AAA"self.assertRegex(text, regex_A3Times)regex_BMoreThan4Times = r"^B{4,}"self.assertNotRegex(text, regex_BMoreThan4Times)text = "BBBBBB"self.assertRegex(text, regex_BMoreThan4Times)regex_CMoreThan4LessThan7Times = r"^C{4,7}"self.assertNotRegex(text, regex_CMoreThan4LessThan7Times)text = "CCCCCC"self.assertRegex(text, regex_CMoreThan4LessThan7Times)text = "CC"self.assertNotRegex(text, regex_CMoreThan4LessThan7Times)def test_matchCharSet(self):regex_include = r"^[abcABC]$"text_A = "A"self.assertRegex(text_A, regex_include)text_d = "d"self.assertNotRegex(text_d, regex_include)regex_uninclude = r"^[^abcABC]$"self.assertNotRegex(text_A, regex_uninclude)self.assertRegex(text_d, regex_uninclude)def test_matchBoundary(self):regex_welcome = r"^welcome\b$"self.assertRegex("welcome", regex_welcome)self.assertNotRegex("welcome to", regex_welcome)regex_difference = r"^different\B"self.assertRegex("different1", regex_difference)self.assertNotRegex("different", regex_difference)def test_matchGroup(self):# 分组regex_group = r"^(ab)+"self.assertRegex("abab", regex_group)self.assertRegex("abcd", regex_group)self.assertNotRegex("cdab", regex_group)regex_group = r"^apple|orange"self.assertRegex("apple", regex_group)self.assertRegex("orange", regex_group)self.assertNotRegex("pear", regex_group)# 捕获分组regex_fullDate = r"^(\d{4})-(\d{2})-(\d{2})$"self.assertRegex("2020-01-01", regex_fullDate)self.assertNotRegex("2020-1-1", regex_fullDate)# 非捕获分组regex_MrMsMrs = r"^(?:Mr|Ms|Mrs)\. (\w+)"self.assertRegex("Mr. Smith", regex_MrMsMrs)self.assertRegex("Ms. Smith", regex_MrMsMrs)self.assertRegex("Mrs. Smith", regex_MrMsMrs)self.assertNotRegex("Mrs.Smith", regex_MrMsMrs)# 命名分组regex_namedCaptureGroup = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"self.assertRegex("2020-01-01", regex_namedCaptureGroup)self.assertEqual(2020, int(re.search(regex_namedCaptureGroup, "2020-01-01").group("year")))self.assertEqual(1, int(re.search(regex_namedCaptureGroup, "2020-01-01").group("month")))self.assertEqual(1, int(re.search(regex_namedCaptureGroup, "2020-01-01").group("day")))self.assertNotRegex("2020-1-1", regex_namedCaptureGroup)# 分组引用# 反向引用regex_reverseReference = r"^(?P<name>\w+) (?P=name)$"self.assertRegex("Tom Tom", regex_reverseReference)self.assertNotRegex("Tom Tom Tom", regex_reverseReference)# 命名反向引用regex_namedReverseReference = r"(?P<word>\w+) (?P=word)"self.assertRegex("Pob Pob", regex_namedReverseReference)self.assertNotRegex("Pob", regex_namedReverseReference)# 替换引用text = "2023-05-15"new_text = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\2/\3/\1', text)self.assertEqual("05/15/2023", new_text)def test_matchSpecialChar(self):regex_dot = r"^a.c$"self.assertRegex("a和c", regex_dot)self.assertNotRegex("a\nd", regex_dot)regex_backslash = r"^a\\b$"self.assertRegex("a\\b", regex_backslash)self.assertNotRegex("a\b", regex_backslash)

3、标志

正则表达式标志,也称为修饰符,是用于改变正则表达式匹配行为的特殊指令,起到指定额外的匹配策略的功能。在 Python,字符串字面量形式的正则表达式,无法直接使用标志,必须借助 re.compile 方法实现。

目前,re 模块支持的正则表达式标志,一共有:

  • re.IGNORECASE (对应 i 标志)
  • re.MULTILINE (对应 m 标志)
  • re.DOTALL (对应 s 标志)
  • re.VERBOSE (对应 x 标志)
  • re.ASCII (对应 a 标志)
  • re.LOCALE (对应 L 标志)
  • re.UNICODE (对应 u 标志)

特别说明,Python 中不支持全局匹配的 g 标志和粘性模式的 y 标志。

import re
import unittestclass TestFlag(unittest.TestCase):def test_ignoreCase(self):"""测试忽略大小写的匹配"""regex_ignore_case = re.compile(r"abc", re.IGNORECASE)regex_not_ignore_case = r"abc"self.assertRegex("aBc", regex_ignore_case)self.assertNotRegex("aBc", regex_not_ignore_case)def test_globalMatch(self):"""测试全局匹配"""text = "abc ABC abc"result = re.search(r"abc", text, re.IGNORECASE)self.assertEqual(result.group(), "abc")self.assertEqual(re.findall(r"abc", text, re.IGNORECASE), ["abc", "ABC", "abc"])result = re.sub(r"abc", "xyz", text,)self.assertEqual(result, "xyz ABC xyz")match_list = re.finditer(r"abc", text)self.assertEqual(2, len(list(match_list)))def test_multilineMode(self):"""测试多行模式:return:"""text = "abc\nABC\nabc"pattern = re.compile(r"abc", re.MULTILINE|re.IGNORECASE)self.assertEqual(pattern.findall(text), ["abc", "ABC", "abc"])def test_dotallModel(self):"""测试单行模式:return:"""text = "abc\nABC\nabc"pattern = re.compile(r"abc", re.DOTALL)self.assertEqual(["abc", "abc"], pattern.findall(text))def test_unicodeMode(self):"""测试unicode模式:return:"""text="αβγ"pattern = re.compile(r"αβγ", re.UNICODE)self.assertRegex(text, pattern)self.assertNotRegex("abc",  pattern)def test_verboseMode(self):"""测试VERBOSE模式"""text = "Price: 12.99 and 5.50"pattern = re.compile(r"""\d+     # 一个或多个数字\.      # 小数点\d*     # 零个或多个数字""", re.VERBOSE | re.IGNORECASE)result = pattern.findall(text)self.assertEqual(result, ["12.99", "5.50"])

对于全局匹配,可以使用 re.search 方法、re.findall 方法、re.sub 方法以及 re.finditer 方法进行替代。

二、运算优先级

使用元字符的正则表达式,如果想实现符合预期的校验功能,必须遵循正则表达式语法所规定的运算优先级。
相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:

运算符描述
\转义符
(), (?😃, (?=), []圆括号和方括号
*, +, ?, {n}, {n,}, {n,m}限定符
^, $, \任何元字符、任何字符定位点和序列(即:位置和顺序)
|替换,“或"操作 字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food”。若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood"。

以下是一些常见正则表达式运算符按照优先级从高到低的顺序:

  • 转义符号: \ 是用于转义其他特殊字符的转义符号。它具有最高的优先级。

    示例:\d\. 等,其中 \d 匹配数字,\. 匹配点号。

  • 括号: 圆括号 () 用于创建子表达式,具有高于其他运算符的优先级。

    示例:(abc)+ 匹配 “abc” 一次或多次。

  • 量词: 量词指定前面的元素可以重复的次数。

    示例:a* 匹配零个或多个 “a”。

  • 字符类: 字符类使用方括号 [] 表示,用于匹配括号内的任意字符。

    示例:[aeiou] 匹配任何一个元音字母。

  • 断言: 断言是用于检查字符串中特定位置的条件的元素。

    示例:^ 表示行的开头,$ 表示行的结尾。

  • 连接: 连接在没有其他运算符的情况下表示字符之间的简单连接。

    示例:abc 匹配 “abc”。

  • 管道: 管道符号 | 表示"或"关系,用于在多个模式之间选择一个。

    示例:cat|dog 匹配 “cat” 或 “dog”。

接下来我们看下以下正则表达式的优先级说明:

\d{2,3}|[a-z]+(abc)*
  • \d{2,3} 匹配两到三个数字。
  • | 表示或。
  • [a-z]+ 匹配一个或多个小写字母。
  • (abc)* 匹配零个或多个 “abc”。

文章转载自:

http://0IS3B69b.gzxnj.cn
http://jai6zbaR.gzxnj.cn
http://mB0NLeCn.gzxnj.cn
http://yEIPkGCZ.gzxnj.cn
http://QeM6kAlF.gzxnj.cn
http://pSQJvxhf.gzxnj.cn
http://YZwNtECy.gzxnj.cn
http://cSpsoDcd.gzxnj.cn
http://RTzYlVFl.gzxnj.cn
http://0KrDC4sU.gzxnj.cn
http://UKXHAMtH.gzxnj.cn
http://lxSUtpoX.gzxnj.cn
http://IpWud7Jn.gzxnj.cn
http://uKcTl9Ro.gzxnj.cn
http://kGEqXwln.gzxnj.cn
http://gOh4kKuJ.gzxnj.cn
http://ipXVaRz8.gzxnj.cn
http://Fa8h3HkZ.gzxnj.cn
http://w1jKSjpP.gzxnj.cn
http://3YXBoVHb.gzxnj.cn
http://mnJOjohg.gzxnj.cn
http://2EvJvLXb.gzxnj.cn
http://J5J2oypP.gzxnj.cn
http://Ebm4YQob.gzxnj.cn
http://b4J0pA2E.gzxnj.cn
http://dzPIhX03.gzxnj.cn
http://5qdSoykD.gzxnj.cn
http://dj7s8xCE.gzxnj.cn
http://zn8O86Qp.gzxnj.cn
http://LzduR8qI.gzxnj.cn
http://www.dtcms.com/a/383745.html

相关文章:

  • Altium Designer(AD24)打开工程文件的几种方法
  • 26考研——内存管理(3)
  • 知识库缺乏维护和清理机制会造成哪些后果
  • android studio 华为 安装app 层层验证
  • 机器学习(三):决策树
  • 气缸夹爪机构分析
  • np.sum(e_x, axis=-1, keepdims=True)
  • kafka--基础知识点--5.3--producer事务
  • SCI论文组成部分
  • 软考 系统架构设计师系列知识点之杂项集萃(146)
  • C语言之函数
  • A050基于博途西门子1200PLC智能交通灯控制系统
  • shell文本处理三核心:grep(过滤匹配)、sed(流编辑)、awk(结构化分析)
  • 【WIT】编程百问一
  • ros2-tf树查看
  • 速通ACM省铜第四天 赋源码(G-C-D, Unlucky!)
  • MFC仿真
  • Leetcode 19 java
  • Vue3 响应式核心 API
  • linux故障排查
  • 为什么哈希表能 O(1) 查找?——C++ 哈希基础入门
  • [CISCN2019 华北赛区 Day1 Web2]ikun
  • 算法——递推求解
  • stm32之点灯
  • Qt---内存管理 对象树(Object Tree)机制
  • 人工智能通识与实践 - 计算机视觉技术
  • GAMES101:现代计算机图形学入门(Chapter1 计算机图形学概述)学习笔记
  • MATLAB 常用函数汇总大全和高级应用总结
  • Knockout.js 任务调度模块详解
  • LeetCode 2414.最长的字母续连续子字符串的长度