正则表达式必知必会

正则表达式必知必会

匹配单个字符

匹配纯文本

相当于文本查找的功能(CMD + F)。但是一般的正则表达式引擎默认返回第一个匹配结果。

大小写匹配

匹配任意字符

使用 . 来匹配任意字符。
譬如:使用 yunis. 来匹配,yunis1yunis2yunis3都是符合搜索结果的。

匹配特殊字符

如果需要匹配特殊字符( . 等)就需要使用元字符( \ 反斜杠)来对他进行转义。
譬如:使用 yunis\. 来匹配, yunis. 是符合匹配结果的。

匹配一组字符

匹配多个字符串中的任意一个

譬如:使用 [abc]hhhh 进行匹配。ahhhh,bhhhh,chhhh 都是符合条件的搜索结果。

例如字符集合区间

譬如:使用[a-c]hhh 进行匹配,ahhhh,bhhhh,chhhh 都是符合条件的搜索结果。
使用 [0-9]hhh 进行匹配 0hhh1hhh2hhh 。。。 9hhh 都是符合条件的搜索结果。

  • A-Z 匹配 A 到 Z 的所有大写字母。
  • a-z 匹配 a 到 z 的所有小写字母。
  • A-F 匹配 A 到 F 的所有大写字母。
  • A-z 匹配从 ASCII 字符 A 到 ASCII 字符 z 的所有字母。
  • 同一个字符集合里面可以给出多个字符区间。譬如 [A-Za-z0-9] 可以匹配任何字母和数字。

取非匹配

可以使用元字符 ^ 来标明你想对一个字符集合取非匹配结果。
譬如:使用 a[\^0-9] 进行匹配, as,ab,ac 都是符合调剂的搜索结果,a0,a1,a2不是符合条件的搜索结果。

使用元字符

对特殊字符使用转义

如果想匹配元字符需要使用 \ 反斜杠来转义。
譬如:匹配 \ 本身就需要转义,需要使用 \\来匹配 \
匹配 [ 需要使用 \[ 来进行匹配。

匹配空白字符

  • \f 换页符
  • \n 换行符
  • \r 回车符
  • \t 制表符(Tab)
  • \v 垂直制表符

匹配数字

  • \d 匹配任意一个数字字符 等价于 [0-9]
  • \D 匹配任意一个非数字字符 等价于 [^0-9]

匹配字母与数字

  • \w 匹配任意一个字母、数字或者下划线字符,等价于 [a-zA-z0-9_]
  • \W 匹配任何一个非字母、数字或者下划线字符,等价于 [^a-zA-z0-9_]

匹配空白字符

  • \s 任意一个空白符 ,等价于 [\f\n\r\t\v]
  • \S 任意一个非空白符 ,等价于 [^\f\n\r\t\v]

重复匹配

匹配一个或多个字符

想要匹配同一个字符或者字符集合的多次重复,只需要简单的给这个字符或字符集合加上一个 + 字符作为后缀就行了。
+ 匹配一个或者多个字符(至少一个,不匹配零个字符的情况)。譬如 a 匹配 a 本身,a+ 将匹配一个或多个连续出现的 a 。类似的 ,[0-9] 匹配任意一个数字,[0-9]+ 匹配一个或多个连续的数字。

匹配邮箱:[\w.]+@[\w.]\.\w+
一般来说,在字符集合里面的元字符将被解释为普通字符,不需要被转义,但转义了也没有坏处。 [\w.][\w\\.] 是一样的。

+ 是一个元字符。如果需要匹配 + 本身,需要转义。

匹配零个或者多个字符

+ 匹配一个或者多个字符,* 匹配零个或者多个字符。
* 是元字符,匹配他本身需要转义。

匹配零个或者一个字符

? 元字符 ? 的意思是匹配一个字符一次或者零次。
譬如:https? 进行匹配,httphttps 都是符合条件的搜索结果。

匹配重复次数

  • +* 匹配的字符个数没有上限。我们无法为他们将匹配的字符个数设定一个最大值。
  • +* 至少匹配零个或者一个字符。无法为他们匹配的字符个数设定一个最小值。
  • 如果只使用 +* 我们无法将他们匹配的字符个数设定为一个精确的数字。
为重复匹配设定一个精确的值

使用 {6} 表示前一个字符匹配6次。
譬如 y{6} 进行匹配,yyyyyy 是符合条件的匹配结果。

为重复匹配设定一个区间

譬如使用 y{2,4} 进行匹配,yy,yyy,yyyy 都是符合条件的搜索结果。

为重复匹配设定一个最小重复数字

譬如使用 y{2,} 进行匹配,yy,yyy,yyyyyyyyyyyyyyyy 都是符合条件的搜索结果。
这个正则的意思是y至少重复2次。

防止过度匹配

下面一段文本 <b>1234</b>qweqweqweqweqweqweqw<b>5678</b>
当我们使用 <b>.*</b> 进行匹配时,我们希望得到的是 <b>1234</b><b>5678</b> ,但是我们得到的是 <b>1234</b>qweqweqweqweqweqweqw<b>5678</b>

这是因为什么呢?
因为 *+ 都是 贪婪型 的元字符,它们进行匹配时的模式是多多益善而不是适可而止。它们会尽可能的从一段文本的开头一直匹配到这段文本的结尾,而不是从文本的开头匹配到第一个匹配时为止。

当不需要这种贪婪型模式时怎么办?使用它们的懒惰型版本,懒惰型版本会尽可能少的匹配字符。

贪婪型元字符 懒惰型元字符
* *?
+ +?
{n,} {n,}?

当我们使用 <b>.*?</b> 进行匹配时,就能得到的是 <b>1234</b><b>5678</b> 的匹配结果。

位置匹配

单词边界

文本 dog hjhjdogkjhkj hhh dogs. 当我们使用 dog 进行匹配时,我们只希望匹配到第一个单词 dog,但是匹配的结果是 dog hjhjdogkjhkj hhh dogs.

这个时间就需要使用单词边界来限定了,单词边界由限定符 \b 表示。

当我们使用 \bdog\b 来匹配时,只会匹配到第一个 dog 单词。

字符串边界

  • ^ 匹配一个字符串的开始。
  • $ 匹配一个字符串的结尾。
  • (?m) 匹配一行的开始。

使用子表达式

子表达式是一个更大的表达式的一部分;把一个表达式划分为一系列子表达式的目的是为了把那些子表达式当作一个独立的元素来使用。子表达式必须使用 () 括起来。
譬如:使用

(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((1\d{1,2})|(\d{1,2})|(2[0-4]\d)|(25[0-5]))

来匹配合法 IP 地址。
这个正则的意思是:

192.168.155.149

  • 任何一个1位或者2位数字 \d{1,2})
  • 任何一个以1开头的3位数字 (1\d{2})
  • 任何一个以2开头、第2位数字在0-4之间的3位数字 (2[0-4]\d)
  • 任何一个以25开头、第3位数字在0-5之间的3位数字 (25[0-5])
  • 然后拼接上 ..
  • 然后重复3次。
  • 最后拼接上一个1到3位数字,这个数字是:
    • 任何一个1位或者2位数字 \d{1,2})
    • 任何一个以1开头的3位数字 (1\d{2})
    • 任何一个以2开头、第2位数字在0-4之间的3位数字 (2[0-4]\d)
    • 任何一个以25开头、第3位数字在0-5之间的3位数字 (25[0-5])

回溯引用:前后一致匹配

先看一个正则表达式:
[ ]+(\w+)[ ]+\1
这个表达式的意思是首先 [ ]+ 匹配一个或者多个空格,然后 (\w+) 匹配一个或者多个字母或者数字字符,然后 [ ]+ 匹配一个或者多个空格,最后 \1 的意思是一个回溯引用,它引用的是前面的表达式的第一个表达式。同理 \2 表示引用的是前面第2个表达式,\3 表示引用的是前面第3个表达式.

(Y)(N).+\2 这个表达式的意思是:
首先匹配一个字母Y,然后匹配一个字母N,然后匹配一个或者多个任意字符,最后的 \2 表示引用之前的第2个表达式,这里引用的就是 (N) 表达式。

这种引用是基于位置的引用,如果表达式的位置发生变化,可能就导致引用失效。

现在一些比较新的正则表达式支持“命名捕获”,支持位子表达式起一个唯一的名字,可以重复使用。

回溯引用在替换操作中的作用

在替换操作中,回溯引用可以起到很大的作用。
譬如下面一段文本

186-123-425
345-456-132
132-345-234

是以XXX-XXX-XXX 的形式来展示的,如果想改成 (XXX)(XXX)(XXX) 的话,只需要先使用 (\d{3})(-)(\d{3})(-)(\d{3})
把之前的符合替换规则的文本找出来,然后使用 ($1)($3)($5) 进行替换就行了。

(186)(123)(425)
(345)(456)(132)
(132)(345)(234)
大小写装换
元字符 说明
\E 结束 \L 或者 \U 装换
\l 把下一个字符装换为小写
\L \L\E 之间的字符全部装换为小写
\u 把下一个字符装换大写
\U \U\E 之间的字符全部装换为大写

下面是一段 html 文本:

<html>
<head>
<title>Head First Lounge</title>
</head>
<body>
<h1>Welcome to the New and Improved Head First Lounge</h1>

<p>![](http://upload-images.jianshu.io/upload_images/234403-fd3e210b176d8c43.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</p>

</body>
</html>

当我们想把 h1标签中的文本全部改为大写时,可以这样操作。

(<h1>)(.*?)(</h1>)

然后

$1\U$2\E$3

前后查找

向前查找

从语法上看,一个向前查找模式其实就是一个以 ?= 开头的子表达式,需要匹配的文本跟在 = 后面。

文本:
http://www.baidu.com
https://yunissong.github.io/

当使用 .+(?=:) 查找时,: 前面的 http https 会被匹配出来。

向后查找

向后查找的操作符是 ?<=

文本:
http://www.baidu.com
https://yunissong.github.io/

当使用 (?<=:).+ 查找时, : 后面的 //www.baidu.com //yunissong.github.io/ 会被匹配出来。

前后查找

<h1>welcome to the New and Improved Head First Lounge</h1>

使用 (?<=<h1>)(.*?)(?=</h1>),就能把 h1 标签之间的内容匹配出来。

对前后查找取非

之前看到过对字符集合取非使用的是 ^ 操作符。但是对去前后查找,取非是使用 ! 来替换 = 取非。

操作符 说明
(?=) 正向前查找
(?!) 负向前查找
(?<=) 正向后查找
(?<!) 负向后查找

嵌入条件

(123)456-7890 和 123-456-7890 都是可以接受的北美号码,而1234567890、(123)-456-7890和(123-456-7890)虽然包含了正确的数字,但是格式不对,如果我们使用下面的正则来匹配:

文本:

123-456-7890
(123)456-7890
(123)-456-7890
(123-456-7890
1234567890
123 456 7890

正则:

\(?\d{3}\)?-?\d{3}-\d{4}

这段正则的意思:

  • \(? 匹配了一个可选左括号;
  • \d{3} 匹配了3位数字;
  • \)? 匹配了一个可选右括号
  • -? 匹配了一个可选的连字符;
  • \d{3} 匹配了3位数字;
  • - 匹配了一个连字符;
  • \d{4} 匹配了4位数字。

符合匹配的结果有:

123-456-7890
(123)456-7890
(123)-456-7890
(123-456-7890

并不能排除不正确格式的电话号码。

正则表达式里的条件

正则表达式里的条件要用 ? 来定义。
之前我们已经见过几种特定的条件了。

  • ? 匹配前一个字符或者表达式,如果它存在的话;
  • ?=?<= 匹配前面或者后面的文本,如果它存在的话;
  • 嵌入语法也使用了?
    • 根据一个回溯引用来进行条件处理。
    • 根据一个前后查找来进行条件处理。

回溯引用条件

回溯引用条件只有在前面一个表达式搜索取得成功的情况下才允许使用的一个表达式。

需要把文本里面 <IMG> 标签全都找去来,不仅如此,如果某个 <IMG> 标签是一个链接 (<A> 标签包裹),还需要把链接匹配出来。

文本:

<!-- Nav bar -->
<TD>
  <A href="/home">![](/images/home.gif)</A>
  ![](/images/home.gif)
  <A href="/home">![](/images/home.gif)</A>
  ![](/images/home.gif)
  <A href="/home">![](/images/home.gif)</A>
  ![](/images/home.gif)
</TD>

正则:
(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)

解析:

  • (<[Aa]\s+[^>]+>\s*)? 将匹配一个 <A><a> 标签,以及标签内部的任何属性,这个标签可有可无,因为后面跟了一个
  • <[Ii][Mm][Gg]\s+[^>]+> 匹配一个 <IMG> (大小写均可) 标签以及任意属性。
  • (?(1)\s*</[Aa]>) 是一个回溯引用条件,?(1) 的意思是:如果第一个回溯引用存在这个例子中就是前面的 <A> 标签),则使用 \s*</[Aa]> 继续匹配。只有前面的 <A> 标签匹配成功,才继续进行后面的匹配。如果(1) 存在,\s*</[Aa]> 将匹配 结束标签 </A> 之前的任意空白字符。
回溯引用条件否表达式

形式:
(?(backreference)true-regex|false-regex)
如果 backreference 成立 执行 true-regex ,不成了,执行 false-regex。

!!!! 本来想用这个查找下代码里面有多少是换行后的{ 多少是空格后的{,Xcode 竟然不支持条件查找!!!.

再来看之前那个电话号码的例子,正则改为:
(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}

解析:

  • (\()? 匹配了一个可选左括号;
  • \d{3} 匹配了3位数字;
  • (?(1)\)|-)
    • ?(1) 判断前面的条件是否成立,( 是否存在。
    • 存在 执行 \)
    • 不存在,执行 -
  • \d{3} 匹配了3位数字
  • - 匹配了一个连字符;
  • \d{4} 匹配了4位数字。

前后查找条件

其实就是之前的回溯引用差不多,不同的就是,这里的回溯条件使用了自定义的子正则表达式。

d{5}(?(?=-)-\d{4})

(?(?=-)-\d{4}) 的意思是如果 (?=-) 成立,则进行后面 -\d{4} 的匹配。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 什么是正则表达式 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),...
    小貔阅读 1,422评论 0 7
  • 正则表达式 regex 语法容易掌握, 如何解决实际问题难。 正则表达式测试器:http://deerchao.n...
    江湖人称向前兄阅读 619评论 0 0
  • 花了一天时间读完正则表达式必知必会,新入入门的确是个好资料。 1.转义字符 \,表示字符有特殊含义,而不是字符本身...
    xbover阅读 343评论 0 0
  • 初衷:看了很多视频、文章,最后却通通忘记了,别人的知识依旧是别人的,自己却什么都没获得。此系列文章旨在加深自己的印...
    DCbryant阅读 3,981评论 0 20
  • 正则表达式到底是什么东西?字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等...
    狮子挽歌阅读 2,134评论 0 9