admin 管理员组文章数量: 887021
了解
正则表达式
啦啦啦 我是一个搬运工,搬错打轻点儿 _-
- 正则表达式
- 正则他祖宗十八代
- 表达式术语
- 流派和特性
- 起源
- Tips
- 起源
- 正则模式&匹配模式
- 一些元字符和特性
- 字符缩略表示法
- 字符组
- 普通字符组 [a-z]和[^a-z]
- 点号
- Unicode组合字符序列: \x
- 字符组简记法 法\w \d \s \w \D \S
- Unicode属性 字母表以及区块 \p{xxx} \P{xxx}
- 字符组减法
- 字符组减法
- POSIX方括号表示法
- 锚点和零点断言
- 匹配的起始位置/上一次结束的位置:\G
- 单词分解符 \b \B \< >
- 环视
- 模块作用范围
- 注释
- 捕获分组 & 分组括号 & 命名捕获
- 固化分组
- 匹配优先量词
- 忽略优先量词
- 正则他祖宗十八代
正则他祖宗十八代
表达式术语
- 正则:正则表达式(regular expression)简写为 regex
- 匹配:正则表达式「a」不能匹配cat,但是能匹配cat中的a
- 元字符:又叫元字符序列,取决于应用的具体情况,「*」是元字符 而「*」是转义 不是元字符
而且不同流派的元字符也不一样,Perl的字符串也有自己的元字符,它们完全不同于正则表达式元字符
- 坑:Perl里面的@表示数组名,需要转义
- 流派:类似方言,匹配规则、优化规则、元字符库什么的都不一样
流派和特性
使用一个正则,不能只会一种,也不能一个工具用到死,适当的了解各种流派的各种特性比较好,需要关注以下三点:
- 元字符库(数量+含义,也就是流派)
- 语言和相应工具的交互方式: 有那些操作、怎么操作、操作的激励(文本)是什么
- 引擎的工作方式:就是实现原理
- 准备好吃的 慢慢水几分钟
起源
- 最初的想法来自 20 世纪 40 年代的两位神经学家,他们研究出一种模型,认为神经系统在神经元层面上就是这样工作的。若干年后,有人在代数学中正式描述了这种被他称为“正则集合”。后来又归纳出了一套简洁的表示正则集合的方法,叫做“正则表达式”。
- 1968年,有人在发明一个正则表达式编译器 qed,并生成了IBM7094的工程代码,后来成为Unix中ed编译器的基础。
- 后来因为ed的一条很牛叉的命令,“g/RegularExpression/p”,读作“Global Regular Expression Print”(应用正则表达式的全局输出)。这个功能非常实用,最终成为独立的工具grep
- AT&T的贝尔实验室之后又产生了egrep——扩展的grep
- egrep演变的同时,其他程序,例如awk、lex和sed,也在按各自的脚步前进,改改功能 加加特性,搞成自己的流派了。
因为个流派太乱,后面出现了POSIX标准,目前为止 几乎所有流派都遵循这个标准。比如local,给出了日期和时间的格式、货币币值、字符编码对应的意义等,让程序变得标准化,虽然不是专门为正则设定的,但是对正则影响灰常大。
后面,出现了一个开发工具,Perl,柔和了众多语言的特性,提供了一套实用的匹配机制。先后进行了5次改版,因为它旨在进行文本处理,而当时兴起的web页面也是字符流,所以马上就火了。后面Python、.NET、Ruby、PHP、C/C++等都发布了一堆正则包(其实就是perl的兼容版),而且1997年还出现了一个套兼容Perl的正则库 PCRE 全面模仿Perl的语法和语义 引擎质量很高。PHP、Apache 2等比较出名的工具 都使用过PCRE。
- 集成式:表达式直接内建在语言中,比如Perl –> $line =~ m/^Subject: (.*)/i,好处就是 让一些操作透明化:例如正则表达式的预处理,准备匹配,应用正则表达式,返回结果
- 程序式 + 面向对象式:普通函数/构造方法,并没有专属于正则表达式的操作符,只有平常的字符串,普通的函数、构造函数和方法把这些字符串作为正则表达式来处理
- JAVA中使用的是java.util.regex 包,Pattern.compile(“^Sujbcet: (.*)”, Pattern.CASE_INSENSITIVE),然后用matcher,然后是find,然后是group等。
Tips
- 字符&组合字符序列:某些看起来的字符,并不是字符,比如à在Unicode中是a和钝重音’构成。这种叫做组字符 组成他们的叫做码点。
- 但是有些程序,字符 = 代码点。比如à(U+0061 加上U+0300)能够由「^..$」匹配,而不是「^.$」
- 不过为保证Unicode和Latin-1 映射,à它也可以用单个代码点U+00E0表示。
- 表示蛋疼。。。多百度
正则模式&匹配模式
下面说的这些模式,可以用于整个表达式 也可以只用于子表达式。
- 常见的/i、/x
- JAVA中的Pattern.CASE_INSENSITIVE
- 子表达式是通过一些正则结构实现的,开始:(?i) 结束:(?-i),有些流派也支持这种(?i:…)和(?-i:…)
下面介绍一些匹配模式,以及一些坑!!!
- 不区分大小写: /i
- 某些字符大小写不是一对一的:希腊字母西格马Σ,它有两个小写形式ζ和σ,目前只有Perl和Java可以处理
- Unicode的 J(U+01F0)没有对应的大写形式的单字符,而且还需要组合字符U+004A 和 U+030C,还有一些一对三的,不过都不是通用字符
- 宽松排列和注释模式:/x.忽略外部的所有空格,字符组内部的空白字符仍然有效(java的regex例外),#符号和换行符之间的内容视为注释
- JAVA中,字符组之外的空格被看成是一个无意义的字符,比如\12 3,是\12后面加了一个3 而不是\123
- 单行模式:/s。点不匹配换行符,此模式就是说点可以匹配任何字符
- 多行文本模式:/m。影响到行锚点「^」和「$」的匹配。
- 通常情况下,锚点「^」不能匹配字符串内部的换行符,而只能匹配目标字符串的起始位置。此模式下,它能够匹配字符串中内嵌的文本行的开头位置
- 通常只能匹配换行符之前的位置,「$」可以匹配字符串内部的换行符!!!!!!
- 「\ A」和「\Z」,它们的作用与普通的「^」和「$」一样,只是在此模式下它们的意义不会发生变化。也就是说「\A」和「\Z」永远不会匹配字符串内部的换行符。有些实现方式中,「$」和「\Z」能够匹配字符串不过它们通常会提供「\z」,唯一匹配整个字符串的结尾位置
- 和单行模式没有一毛钱关系0.0
- 文本匹配模式:不识别任何表达式元字符,搜索这个字符串 而不是匹配这个正则表达式!!!!!!!
一些元字符和特性
这就是一些总结,算是正则的梳理吧,没有水 放下零食 准备接受干货吧
字符缩略表示法
制表符 | 介绍 |
---|---|
\a | 在“打印”时扬声器发声,ASCII中的BEL字符,八进制编码007 |
\b | 退格 通常对应ASCII中的BS字符,八进制编码010,某些地方只有字符组内才有用,其他表示单词分界符 |
\e | Escape字符 通常对应ASCII中的ESC字符 |
\n | 换行符 |
\r | 通常对应ASCII的CR字符。IOS中对应到ASCII的LF字符 |
\t | 水平制表符 对应ASCII的HT字符 |
\v | 垂直制表符 对应ASCII的VT字符 |
\f | 进纸符 通常对应ASCII中的字符(问号脸???) |
不过类似 m r这种,随系统变化的,可以使用\012(比如HTTP协议中)
字符组
普通字符组 [a-z]和[^a-z]
- 元字符:在字符组内 *不是元字符,二 - 是元字符,\b 在字符自内外也不一样
- [^LMNOP]等价于[\x00-kQ-\xFF],但是在在Unicode之类字符的值可能大于 255(\xFF)的系统中,前者只是不包括L、M、N、O和P
- [a-zA-Z] 不一定等于 [a-Z]
点号
- java的regex包,不能匹配行终结符
- 匹配模式,会修改匹配规则
- POSIX,不能匹配nul(值为0的字符)
- [^x]是可以匹配换行符的
Unicode组合字符序列: \x
通常情况,表示\P{M}\p{M}*,可以匹配换行符+Unicode行终结符,但是不能匹配以组合字符开头的字符(这里可以和点好比较一波)
字符组简记法 法\w \d \s \w \D \S
标记 | 介绍 |
---|---|
\d | 数字 登记于[0-9] |
\D | 非数字 [^\d] |
\w | 单词中的字符,[a-zA-Z0-9],有些流派不支持下划线,但是都支持locale \p{L}等 |
\W | 非单词字符 |
\s | 空白字符,等价于[空格\f\n\r\t\v],支持Unicode的换行符 U+0085 |
\S | 非空白 |
Unicode属性 字母表以及区块 \p{xxx} \P{xxx}
属性表,貌似和字母表差不多 如下
区块
- 类似字母表,就是某一范围的代码点,比如Tibetan块,表示从U+0F00到U+0FFF的256个代码点。Perl和java.utn.regex中可以用\p{ InTibetan)来匹配,在.NET中的是\p{ IsTibetan)
- 区块一般是按照书写系统来的,比如拉丁语、希伯来语、特殊字符(货币、箭头、印刷符号)
字符组减法
比如:[a-z] - [aeiou] \p{P} - [\p{Pe}\p{Ps}]
字符组减法
比如:[a-z] && [aeiou] ,这里也可以是OR和AND 对应就是[adsfa] = [[asd][fa]] [[asd] && [fa]] ,前者是加 后者是交集
或者是多选结构: aaa|bbb,表示多分支
POSIX方括号表示法
我们说的字符组,在该标准中 是方括号表达式,其中也有一些约定,比如[:lower:]表示locale中的所有小写字母,a-z
- 只有在方括号表达式内才有用, [[:lower:]]
- 尽管locale会变,但是这些一般都会支持的
锚点和零点断言
- 行 字符串起始位置:^ \A
- 行 字符串起始位置:$ \Z \z
- 文本结束为止/换行之前的位置,但是匹配模式会修改 文 本 结 束 为 止 / 换 行 之 前 的 位 置 , 但 是 匹 配 模 式 会 修 改 含义 匹配任何换行符
匹配的起始位置/上一次结束的位置:\G
对迭代很有用的,可以匹配上一次匹配结束的位置
- 第一次迭代 锁定开头 和\A一样
- 匹配不成功,重新指向起始位置,这样如果重复使用某个表达式 就不会受前一次的影响了
在某些流派中,有一些特性(比如Perl)
- 指向目标字符串的属性,而不是这设置这个位置的表达式的属性,这样N个表达式就都可以匹配同一个字符串了,都是从上一轮匹配之后设置的G位置
- 有些给出/c,在匹配失败后不重新设定位置(也就是开头)
这里出现一个问题:这样的匹配是之前结束的位置?还是当前匹配开始的位置呢?很多时候是一样的,但是有个特例:用 x? 匹配 ‘abcde’。
不会报错,但是没有匹配到任何文本,匹配位置就是开头,如果进行全局查找-替换时,正则表达式会重复应用,每次处理上一次操作之后的文本 就会死循环,因为“上次匹配完成的位置”总是它开始的位置
为了避免无穷循环,有些流派的传动装置会强行前进到下一个字符,比如: ‘abcde’应用s/x?/!/g之后,为 !a!b!c!d!e!
单词分解符 \b \B \< >
环视
模块作用范围
前面讲的 (?I:…..)
注释
(?#…) 或者 #…
捕获分组 & 分组括号 & 命名捕获
- 捕获分组:(…) \1 \1,JS里面可以是 $1 $2
- 分组括号: (?:…..),非捕获型括号,表达式清晰 多个儿子构建表达式
- 命名捕获:(?<\name>) group(“name”)
- .NET\k<\name> —这里name前没有\
- js中的命名捕获
固化分组
&aafda! 匹配 &.*!,到结尾 然后因为要匹配! .会释放某些内容,到最短匹配位置为止
- 占有优先量词:*+ ++ ?+ {1,3}+,目前只有java的regex包 PCRE PHP支持
匹配优先量词
* + ? {1,3}
忽略优先量词
*? +? ?? {1,3}?
本文标签: 了解
版权声明:本文标题:了解 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1687328029h90052.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论