正则表达式

正则表达式

用正确的语言,描述问题的模式

问题:

  • 需求:列举全部JPEG图片 ls *.jpg
  • 描述:小数点之前任意字符均可、一小数点接jpg结尾。
  • 需求:列举测试输入和答案文件ll *_test_?.(inlans)
  • 描述:文件名以任意数量、任意字符开始,后接_test_,后接1个任意字符,然后是小数点,以in或ans结尾。
  • 需求:文件批量改名 rename ‘s/LC/lc/’ LC-*.cpp
  • 描述:将LC-开头的cpp文件替换成小写lc并去掉连字符。
  • 需求:标识符 _stdcall valueOf bindlst
  • 描述:以字母或者下划线开头,后续字符还可以是数字。
  • 地址:1号楼2单元301室
  • 描述:数字、“号楼”、数字、“单元”、数字、“室”
  • 需求:URL的http://localhost:9090/search?id=1
    • 描述:单词开头、“:”、双斜线、、接字母或连字符、可选的冒号接数字、斜线之后有任意字符、如果有?则后面有些&分开的等式。

抽象建模:

  • 匹配(match):验证文本是否完全符合模式。
    • 案例:验证字符串是合理的IPv4地址
  • 搜索(search):从文本中找目标模式。可以出现多次。
    • 案例:Web爬虫需要从HTML中找出所有的URL递归爬取。
    • 案例:从病毒DNA搜索基因片段。生物信息学、医疗、考古、刑侦…
  • 替换(replace):将与模式匹配的部分替换成其他内容。文本编辑、文本处理…
    • 案例:将“email, name”替换成“name<email>”制作通讯录。
image-20220428092630352 image-20220428092716206

用正确的语言,描述问题的模式

image-20220428092923075 image-20220428092946633 image-20220428093033532 image-20220428093112755

概念

字母表

  • 问题:怎么确定那些符号可用?
    • 例如二进制数字只能由0和1字符组成。
  • 字母表(alphabet)有限的符号集合。
  • 示例:
    • 二进制字母表{0,1};
    • 十六进制字母表{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F};(字母还有小写)
    • 因为字母(26)、ASCII(128)、Unicode(144697)
    • IPv4字母表{0,1,2,3,4,5,6,7,8,9,.};
    • C++标识符字母表{大写字母、小写字母、下划线、数字}
    • DNA由四种碱基组成,可视为{A,T,G,C}

  • 串(string):基于某个字母表中符号的有穷序列。
  • 示例:
    • 空串
    • 十六进制串DEAD表示数值57005
    • 英文字母串DEAD在英语中表示死亡。
    • 标识符字母表上的串3D不是合理的标识符
    • 基于IPv4字母表的串01.999…“不是合理的IPv4语言”
    • 英语字母串You can you up不是合理的英语。
      • 这个串不属于规范的英语。//属于 ~ 集合 ~ 语言
      • 这个串不符合英语语法。//语法 ~ 模式 ~ 语言

语言

  • 语言(language):给定字母表上一个任意的可数串集合
    • 语言是个集合
    • 可数:可能无穷(与自然数集等势,一一对应)。
  • 示例:
    • 空集∅是语言。
    • 只有空串s的集合{ s }是语言。
    • 字母表上的美中字母都是一种语言。
    • 0和1是二进制字母表上的两种语言。
    • A,T,G,C是DNA字母表上的四种语言。

应用:匹配字符

  • 案例:某国想取缔字母Z,要从所有文件中搜索Z并删除。
  • 方法:字符是基础的正则表达式,直接写就行。
  • 示例:Z被某些国家认为是种邪恶的语言。
  • 说明:两边斜线是分割符,用于区分,不属于正则表达式。

运算

连接 XY

  • 串的连接(concatenation):串X与串Y的连接就是X后接Y。
    • 示例:jpg是j、p、g的连接。
  • 空串是连接运算的单位元。
    • 示例:A连接空串等于A
    • 示例:空串连接A等于A
  • 连接看做乘法,多次连接看做指数运算。
    • 示例:A0是空串
    • 示例:A3是AAA

应用:匹配序列

  • 问题:怎样找特定序列?例如jpg
  • 语言连接:语言L和语言M的连接是L中的任意串与M中的任意串连接的集合。
    • 示例:语言j、语言p、语言g的连接是语言jpg
    • 示例:语言p、语言n、语言g的连接是语言png
  • 通过连接运算可以构造新语言。
    • 示例:语言jpg正确描述了问题的模式。

并 |

  • 问题:想找出扩展名是jpg或png的图片。
  • 思路:既然语言是集合,那么就可以求并集。
  • 方法:运算符 | 表示两种语言的并。
  • 示例:语言 jpg|png正确描述了上述问题的模式。

闭包 *

  • 问题:描述不含0的二进制串。
  • Kleene闭包(closure):语言L连接0次或多次得到的串集合,记为L*
    • 示例:1*描述了问题的模式。
    • 示例:L0是只含空串的集合{ };
  • 正闭包:语言L连接正整数次得到的串集合,记为L+
    • 示例:除非L包含空串,否则L+不含空串;

正则表达式

  • 正则表达式r代表一种语言L®
    • 空串是正则表达式。
    • 字母表中的每个符号都是正则表达式。
  • 递归构造:
    • ® 语言L®本身,括号不影响含义。
    • ®* 闭包
    • ®(s) 连接
    • ®|(s) 并

结合性与优先级

  • 闭包、连接、并运算都是左结合,优先级依次降低。
  • 示例:
    • jpg == (jp)g == j(pg)
    • 10* == 1(0*) != (10)*
    • jpg | png == (jpg) | (png)!= jp(g|p)ng
  • 规定优先级后,可以省略部分括号:
    • (a)|((b)*©)
    • (a)|(b*c)
    • a|b*c
image-20220428111721684

扩展语法

  • 字符

    • 任意字符 .
    • 列举字符 [aeiou]
    • 限定范围 [0-9]
    • 排除范围 [^0-9]
    • 常用类别 数字\d 非数字\D 单词\w 非单词\W 空白\s 非空白\S
  • 频度

    • 零次或一次 ?
    • 零次或多次 *
    • 一次或多次 +
    • 固定n次{n} m到n次{m,n} 至少m次{m,} 最多n次{,n};
  • 定位:开头^ 结尾$ 边界\b

  • 聚组:()任选模式(A|B) 替换\0 \1 \2 … 或 $0 $1 $2 …

匹配任意字符 .

  • 需求:想搜索歼灭机型号,类似J-后接两个任意字符。

  • 方法:元字符 . 匹配任意字符。

  • 解释: . 是(a|b| … )列举字母表所有字符的简写。

  • 示例: /J-…/ 匹配J-7A J-20 …

  • 需求:怎样匹配数点本身?比如搜索带数点的条目。

  • 方法 :反斜线转义 \.

  • 示例:/.\./ 匹配 1. A. 等等

匹配部分字符 [ ]

  • 问题:怎样限定字符?比如只允许元音字母?

  • 方法:在方括号中列举可接受的字符。

  • 解释:[abc]是(a|b|c)的简写。

  • 示例:匹配元音 [aeiou]

  • 示例:匹配数字 [0123456789]

  • 提示:字符顺序无关 [ab]与[ba]等效

指定字符范围 [A-Z]

  • 问题:匹配字母难道要列出52个字符?
  • 方法:用起-止字符指定连续的字符范围。
  • 解释:[a-z]是(a|b|…|z)的简写。
  • 示例:[0-9]数字
  • 示例:[-]连字符本身
  • 示例:标识符由字母或下划线开头[A-Za-z_]

排除字符集 [^]

  • 问题:匹配辅音[b-df-hj-np-tv-z]的写法太啰嗦啦!
  • 方法:异或^开头的字符集只匹配范围以外的字符。
  • 示例:匹配辅音 [^aeiou]
  • 示例:匹配除空格以外的字符[^ ]

频度元字符 ? * +

  • 零次或多次 * 可空也可重复
  • 一次或多次 + 必要也可重复
  • 零次或一次 ?可空但不重复
  • 示例:图片文件名不能为空,扩展名为jpg或jpeg
    • /.+.jpe?g/
  • 示例:匹配C++标识符
    • /[a-zA-Z_][a-zA-Z_0-9]*/

头文件是<regex>

除了性能差,别的啥都好。

例子:

#include<iostring>
#include<regex>
using namespace std;

int main(){
    const regex ptn{"^[A-Za-z_]\\w*$"};
    for(string s; getline(cin, s);){
        cout << regex_match(s, ptn) << endl;
    }
    return 0;
}
//c++标识符的判断;

固定重复次数 { n }

  • 问题:匹配手机号要写11遍吗?身份证号岂不要写18遍?
  • 方法:大括号包围的数字表示之前的重复次数。
  • 示例:手机号是1开头的11位数字 1[0-9]{10}
  • 示例:身份证号的前17位必须是数字,最后1位是数字或X [0-9]{17}[0-9X]

可变重复次数 {m,n}

  • 问题:重复次数不是定值而是范围,怎么办?
    • 比如要求邮箱用户名最少2个,最多20个字符。
  • 方法:{m,n}重复最少m次、最多n次。
  • 方法:{m,}重复最少m次。
  • 方法:{,n}重复最多n次。
  • 示例:用户名字母开头,后面也可以是数字,总长2到20。

​ /[a-zA-Z][a-zA-Z0-9]{1,19}/

预设字符类

  • 需求:ISBN书号要反复写同样字符串集很麻烦。能简化吗?
  • 预设字符类:
    • \d \D 数字/非数(digit) \d相当于[0-9]
    • \w \W 构词/非词(word) \w成词意思是符合标识符的标准 \W是一些符号
    • \s \S 空白/非空(space) \s空白不只是空白,Tab啥的可能也算,但是不同环境下的正则表达式的预设字符不同
  • 上例:\d{3}-\d-\d{4}-\d{4}-\d
  • 示例:标识符 [A-Za-z_]\w*

模式定位

  • 需求:想要匹配完整的行或者单词
  • 方法:使用定位元字符。
    • 匹配开头 ^
    • 匹配结尾 $
    • 匹配单词边界 \b bound边界
  • 示例:/\b([A-Za-z_][A-Za-z_0-9])\b/

子模式()

  • 需求:怎样表达模式中重复的子模式?
    • 比如域名 aaa.bbb.cc@sdu.edu.cn
    • 比如地址 123.45.6.7
  • 方法:子表达式就是子模式。子模式也可以指定频度。
  • 示例:域名\w+(\.\w+)*@sdu\.edu\.cn
  • 示例:地址\d{1,3}(\.\d{1,3}){3}

应用:IPv4的各节

  • 需求:匹配0到255的整数但不能有前缀0.
  • 策略:分而治之。
  • 模式:/\d|[0-9]\d|1\d{2}|2[0-4]\d|25[0-5]/
  • 分组:
    • 一位数:接受
    • 两位数:1-9开头的接受
    • 三位数:1开头的任意、20x到24x、250到255。
  • 拓展:能挣钱描述一节,描述IP地址也就不难了。

惰性匹配

  • 问题:想分出龙新型号“3A3000”的主机号、数字。
  • 试解:/^(\w*)(\d+)$/ ->3A3000
  • 正解:/^(\w***?**)(\d+)$/ ->3A3000
  • 贪婪匹配:尽量多向前匹配。试解
  • 惰性匹配:尽量少向前匹配。正解

替换 $n \n

  • 引用组号:
    • $1 … $9
    • \1 … \9
  • 引用整体:$0或者 \0
  • TODO

特殊字符

  • 移植性 TODO
  • \n 换行(newline)
  • \r 回车(carriage return)
  • \p
  • \t 水平制表
  • \f
  • \v 垂直制表
  • \b

看具体环境,有的支持有的不支持

匹配选项

  • g 全局搜索(global search)
  • i 忽略大小写(case insensitive)
  • m 多行匹配(multiline)
image-20220428214906592 image-20220428215115483 image-20220428215159283 image-20220428215214513

局限性

  • 问题:判断串是否是回文。例如:101
  • 问题:判断身份证号是否有效。考虑校验位和日期。
  • 问题:判断是否质数序列。例如:2_7_5

代价

  • 可能包含递归、产生大量运行时开销,有安全风险(DDoS)
  • 程序编译时间延长
  • 运行时创建regex对象开销较大。
    • 尽量事先创建,重复使用。
  • 性能不如精心编写的专用函数。

最后

image-20220428215657504 image-20220428215802767 image-20220428221819389
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值