本文写于 2020 年 9 月 8 日
“抄自”正则表达式 30 分钟入门。
最重要的是——请给我 30 分钟,如果你没有使用正则表达式的经验,请不要试图在 30 秒内入门——除非你是超人 ?? —— 出自原文
如果你用过 Windows,你很可能见过这样一个东西:*.doc。这是用来匹配所有的 doc 文档的,* 叫做「通配符」。顾名思义,就是所有以 .doc 结尾的文件,通通匹配。
正则表达式与这类似,即通过符号规则,对字符串进行更为精确的匹配。
he,这就是一个非常简单的正则表达式,但他匹配的是 he,hello, hey, her, he, she......真的不太精确。
我们如果只想匹配 he,就需要加上一些条件:\bhe\b。
这里的 \b 就是一个正则表达式的规则(叫做元字符),代表单词的开头或者结尾。
需要注意的是,这里 \b 匹配的并不是空格或者逗号之类的字符,而仅仅是匹配单词的开头、结尾的「位置」。
我们想要找到一句话中,相邻的两个单词:hello world。这该怎么做呢?
使用 \bhello\b *\bworld\b。
别急着晕!这其实并不难。
前半部分和后半部分我们都认识,两对 \b 分别框住了 hello 与 world,是刚刚学到的匹配单词始末的符号。
那么问题就是中间的 *,空格我们不陌生,主要就是这个 *。
* 是元字符(\b 就是元字符),但它跟前面的两者不同——它不代表符号,也不代表位置,代表的是数量。它意味着在自己之前的内容可以连续重复任意次数,使得整个表达式得到匹配。
也就是说这就意味着我们可以匹配 hello world, hello world, hello 一百万个空格 world!中间任意多的空格我们都可以忽略。
+ 也有同样的作用,但与 * 不同的是,+ 号只能匹配重复一次或以上;* 则可以匹配 0 次。
我想寻找 a 开头的字符串该如何?
\ba\w*\b 即可。\w 顾名思义,代表一个 word。我们寻找「字符起始-a-任意个任意字符-结束」就可以找到 a 开头的字符串了。
经常会有 start 啦啦啦,啦啦啦 end 这样的段落,前面一个标签、后面一个标签。
如果我们想要匹配这两个标签以及之间的所有东西该怎么办?
用 .,\bstart\b.*\bend\b 即可。
. 也是一个元字符,它匹配的是除了换行符意外的任意字符。比如字母 a, b;符号 &, 。, !;空格 ......他都能匹配。
比如 .* 就可以匹配整段文字!因为 . 代表非换行符,* 代表重复任意次数,那么此时就会匹配最大次数的非换行符,也就是整段文字。
对于这里,就会匹配 start 与 end 以及中间的所有字符。
\b 表示一个数字,那我如果需要匹配电话号码,岂不是需要 \d\d\d\d\d\d\d\d\d\d\d ?
当然不可以。
我们可以写成 \d{11} 就可以了。
有时候我们需要模糊匹配,例如昵称不得超过 12 个字符,但也不得少于 3 个字符,我们就可以使用 ^.{3,12}$ 了。逗号后不要加空格
这里 ^ 代表句子开头,$ 代表句子结尾,. 代表任意换行符外的字符,{3,12} 代表 . 匹配的字符数量为 3 到 12 个。
那我们来匹配 QQ 号试一试吧。
对于 QQ 号而言,首先有一个最短位数,姑且当他是 5 吧。其次 QQ 号不可以以 0 开头。
那我们开头就可以写 [1-9],后面写上 \d{4,} 就可以了。
这里的 [] 可以理解是一个集合。你想要匹配什么符号,放进去就可以了。
例如 [,!?] 可以匹配三个符号,[0-9] 的意思和 \d 是一样的。
但这只是单个符号的重复,我们希望几个符号成组的重复,就需要 (\w\d-5003){5} 这种写法了。
他的意思就是,将「一个字母 一个数字 -5003」重复五次。
如果需要同时匹配多个国家的手机号码,需要怎么办呢?
正则A|正则B 就可以了。不要带空格
我们还会经常遇到一个问题,比如:不带 a 字符的单词。
正则表达式当然可以轻易的完成这种需求,例如 \W 匹配任意不是字母,数字,下划线,汉字的字符;\S 匹配任意不是空白符的字符;\D 匹配任意非数字的字符\B 匹配不是单词开头或结束的位置;[^x] 匹配除了 x 以外的任意字符;[^aeiou] 匹配除了 aeiou 这几个字母以外的任意字符。
这个和之前的 {2} 可不一样,对于 {2} 而言,我们是无法判断两个重复的任意字符的。
这个时候就需要后向引用了。
后向引用用于重复搜索前面某个分组匹配的文本。什么意思呢?
还是看标题的问题,我们如何用正则做到这一点?
([a-zA-Z])\1。
首先不看小括号,[a-zA-Z] 指匹配一个字母,用括号扩起来就是一个「组」。整个正则从左到右,由小括号扩起来的就是一个组,编号是从 1 开始计算。(分组 0 对应整个正则表达式)
我们用 \1 表示之前组 1 匹配到的东西——也就是组 1 匹配到的字符。这就重复了一个字母两次了。
当然这样一个个数组数是很痛苦的,而且不可读。正则表达式可以通过 (?<组名>正则表达式) 将该组命名为尖括号内的名字了。(也可以用 ‘‘ 代替 <>)
我们会经常去查找在某些内容之前或之后的东西,但并不包括该内容,类似于 \b ^ $。
(?=表达式) 也叫零宽度正预测先行断言,是不是看不懂?他的意思就是我要匹配:该条件出现的位置的后面能匹配表达式。
还是看不懂?
例子来了:匹配以 ing 结尾的单词,但是不要 ing。
\b\w+(?=ing\b)。
现在理解了吧,也就是我们将不想要的结尾,丢到 (?=) 里面,就可以即匹配到,又不带上它。
除了结尾,我们还有开头:(?<=exp),也叫零宽度正回顾后发断言。
还是例子,我们需要匹配 re 开头的单词,但我不要 re。
(?<=\bre)\w+\b。
我们将不想要的开头,丢到 (?<) 里面,就可以即匹配到,又不带上它。
同样,和「反义」一样,我们一样拥有负向零宽断言,可以确保某个字符没有出现,但并不去匹配它。
(?!表达式)
\d{3}(?!\d) 即为匹配三位数字,并且这三位数字的后面不能是数字。
(?<!exp)
(?<![a-z])\d{7} 匹配前面不是小写字母的七位数字。
当正则表达式中包含能接受重复的限定符时,他会去匹配尽可能多的字符。
以这个表达式为例:a.*b。
它将会匹配最长的以 a 开始,以 b 结束的字符串。
如果用它来搜索 aabab 的话,它会匹配整个字符串 aabab。
这被称为「贪婪匹配」。
但有时,我们更需要「懒惰匹配」,也就是匹配尽可能少的字符。
前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。
这样 .*? 就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。
例如 a.*?b 匹配最短的,以 a 开始,以 b 结束的字符串。
如果把它应用于 aabab 的话,它会匹配 aab(第一到第三个字符)和 ab(第四到第五个字符)。
我们还可以用到重复上,{n,}?,意为重复 n 次以上,但尽可能少重复。
(?#comment),小括号加上 ?# 就能写上注释。
如何匹配身份证号?
^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$
我们如何匹配 email 地址?
[\w!#$%&‘*+/=?^_`{|}~-]+(?:\.[\w!#$%&‘*+/=?^_`{|}~-]+)_@(?:[\w](?:[\w-]_[\w])?\.)+[\w](?:[\w-]*[\w])?
我们如何匹配网址?
[a-zA-z]+://[^\s]*
(完)
原文:https://www.cnblogs.com/xhyccc/p/13632330.html