乐趣区

关于java:30分钟玩转正则表达式

简介

举荐浏览:Jeffrey Friedl《精通正则表达式(第 3 版)》,本文是该书的 读书笔记

定义

正则表达式:regular expression, regex,是用来形容或者匹配一系列合乎某个句法规定的字符串的单个字符串。正则表达式这个概念最后是由 Unix 中的工具软件(例如 sed 和 grep)遍及开的。

分类

  • BREs, 根本的正则表达式(Basic Regular Expression)
  • EREs, 扩大的正则表达式(Extended Regular Expression)
  • PREs, Perl 的正则表达式(Perl Regular Expression)

Linux 罕用文本工具

只有把握了正则表达式,能力全面地把握 Linux 下的罕用文本工具(例如:grep、egrep、GUN sed、Awk 等)的用法。

grep, egrep

1)grep 反对:BREs、EREs、PREs 正则表达式

    - grep 指令后不跟任何参数,则示意要应用”BREs“- grep 指令后跟”-E" 参数,则示意要应用“EREs“- grep 指令后跟“-P"参数,则示意要应用“PREs"

2)egrep 反对:EREs、PREs 正则表达式

    - egrep 指令后不跟任何参数,则示意要应用“EREs”- egrep 指令后跟“-P"参数,则示意要应用“PREs"

3)grep 与 egrep 正则匹配文件,解决文件办法

    a. grep 与 egrep 的解决对象:文本文件
    b. grep 与 egrep 的处理过程:查找文本文件中是否含要查找的“关键字”(关键字能够是正则表达式),如果含有要查找的”关健字“,那么默认返回该文本文件中蕴含该”关健字“的该行的内容,并在规范输入中显示进去,除非应用了“>" 重定向符号,
    c. grep 与 egrep 在解决文本文件时,是按行解决的

sed

1)sed 文本工具反对:BREs、EREs

    - sed 指令默认是应用 "BREs"
    - sed 命令参数“-r”,则示意要应用“EREs"

2)sed 性能与作用

    a. sed 解决的对象:文本文件
    b. sed 解决操作:对文本文件的内容进行 --- 查找、替换、删除、减少等操作
    c. sed 在解决文本文件的时候,也是按行解决的

Awk(gawk)

1)Awk 文本工具反对:EREs

    - awk 指令默认是应用“EREs"

2)Awk 文本工具解决文本的特点

    a. awk 解决的对象:文本文件
    b. awk 解决操作:次要是对列进行操作

匹配单个字符

Ben 是一个正则表达式。正则表达式能够蕴含纯文本(甚至能够只蕴含纯文本)。

匹配纯文本

文本

Hello, my name is Ben. Please visit my website at http://www.forta.com/.

正则表达式

Ben

后果

匹配任意字符

.字符能够匹配任何一个单个的字符。

文本

sales1.xls
sales2.xls
sales3.xls
na1.xls
na2.xls
orders3.xls
apac1.xls
europe2.xls

正则表达式

sales.

后果

匹配一组字符

匹配多个字符中的某一个

sales1.xls
sales2.xls
sales3.xls
na1.xls
na2.xls
sa1.xls
ca1.xls
orders3.xls
apac1.xls
europe2.xls

正则表达式

[ns]a.\.xls

后果

应用字符区间

在应用正则表达式的时候,会频繁地用到一些字符区间(0-9、A-Z)。为了简化字符区间的定义,正则表达式提供一个非凡的元字符:-作为连字符。

正则表达式

[ns]a[0-9]\.xls

后果

匹配任何一个字母(无论大小写)或数字

[A-Za-z0-9]

取非匹配

字符汇合通常用来指定一组必须匹配其中之一的字符。然而在某些场合下,咱们须要反过来做,给出一组不须要失去的字符。换句话说:除了那个字符汇合里的字符,其余字符都能够匹配,应用字符^

正则表达式

[ns]a[^0-9]\.xls

后果:上述输出没有后果,因为没有匹配字符串。

小结

元字符 []用来定义一个字符汇合,必须匹配该汇合里的字符之一。定义一个字符汇合的具体方法有两种:

  1. 把所有的字符都列举进去
  2. 利用元字符 - 以字符区间的形式给出

字符汇合能够用元字符 ^ 来求非,这将把给定的字符汇合强行排除在匹配操作外——除了该字符汇合里的字符,其余字符都能够被匹配。

应用元字符

对特殊字符进行本义

元字符是一些在正则表达式里有非凡含意的字符。英文句号 . 是一个元字符,用来匹配任何一个单个字符;左方括号 [ 也是一个元字符,示意一个字符汇合的开始。

因为元字符在正则表达式中有非凡的含意,所以这些字符无奈代表它们自身。须要在元字符的后面加上一个 反斜杠 进行本义——转义序列 \. 将匹配 . 自身。

文本

\home\ben\sales\

正则表达式

\\

后果

匹配空白字符

在进行正则表达式搜寻的时候,咱们常常会遇到须要对原始文本里的非打印空白字符进行匹配的状况。比方找出所有的制表符或换行符,这类字符很难被间接输出到正则表达式里,能够用如下的非凡元字符来输出。

| 元字符 | 阐明 |
| :——: |:———:|
| [\b] | 回退(并删除)一个字符(Backspace 键)|
| \f | 换页符 |
| \n | 换行符 |
| \r | 回车符 |
| \t | 制表符 |
| \v | 垂直制表符 |

文本

"101","Ben","Forta"

"102","Jim","James"

正则表达式

\r\n\r\n

后果

"101","Ben","Forta"
"102","Jim","James"

剖析
\r\n 匹配一个“回车 + 换行”组合,Windows 操作系统都把这个组合作为文本行的完结标签。应用正则表达式 \r\n\r\n 进行的搜寻将匹配两个间断的行尾标签,正是两条记录之间的空白行。

匹配文本完结标签

  • Windows:\r\n
  • Linux:\n

同时实用于 Windows 和 Linux 零碎的正则表达式,应该蕴含一个 可选的 \r和一个 必须被匹配的 \n

匹配特定的字符类别

字符汇合(匹配多个字符中的某一个)是最常见的匹配模式,而一些罕用的字符汇合能够用 非凡元字符 来代替。

匹配数字(非数字)

元字符 阐明
\d 任何一个数字字符(等价于[0-9])
\D 任何一个非数字字符(等价于1

匹配字母和数字(非字母和数字)

元字符 阐明
\w 任何一个字母数字字符或下划线字符([a-zA-Z0-9_])
\W 任何一个非字母数字字符或下划线字符(2

文本

11213
A1C2E3
48075
M1B4F2

正则表达式

\w\d\w\d\w\d

后果

匹配空白字符(非空白字符)

另一种常见的字符类别是 空白字符

元字符 阐明
\s 任何一个空白字符(等价于[\f\n\r\t\v])
\S 任何非一个空白字符(等价于3

小结

次要解说用来匹配特定字符(制表符、换行符)和用来匹配一个字符汇合或字符类(数字、字母数字字符)的元字符。这些简短的元字符能够用来简化正则表达式模式。

反复匹配

有多少个匹配

须要一种可能匹配多个字符的办法,能够通过几个非凡的元字符来实现。

匹配一个或多个字符

要想匹配同一个字符(或字符汇合)的多次重复,只有简略地给这个字符(或字符汇合)加上一个 + 字符作为后缀就能够了。+匹配 一个或多个字符(至多一个,不匹配零个字符的状况)。

比方:a 匹配 a 自身,a+ 将匹配一个或多个间断呈现的 a。

文本

Send personal email to ben@forta.com. For questions 
about a book use support@forta.com. Feel 
free to send unsolicited email to spam@forta.com.

正则表达式

\w+@\w+\.\w+

后果

这个模式把原始文本里的 3 个电子邮件地址全都正确匹配进去了。正则表达式中第一个 \w+ 匹配一个或多个字母数字字符,再用第二个 \w+ 匹配 @前面的一个或多个字符,而后匹配一个. 字符(应用转移序列.),最初用第三个 \w+ 匹配电子邮件地址的残余局部。

匹配零个或多个字符

+匹配一个或多个字符,但不匹配零个字符——+ 起码也要匹配一个字符。那么,如果你想匹配一个可有可无的字符——也就是该字符能够呈现零次或屡次的状况,你该怎么办呢?

这种匹配须要用 * 元字符来实现,把它放在一个字符(或一个字符汇合)的前面,就能够匹配该字符(或字符汇合)间断呈现零次或屡次的状况。

文本

Hello .ben@forta.com is my email.

正则表达式

\w+[\w.]*@[\w.]+\.\w+

后果

\w+:负责匹配电子邮件地址中第一个字符(一个字母数字字符,然而不包含.)。
[\w.]*:负责匹配电子邮件第一个字符之后、@字符之前的所有字符——这个局部能够蕴含零个或多个字母数字字符和. 字符。

匹配零个或一个字符

?只能匹配一个字符(或字符汇合)的零次或一次呈现,最多不超过一次。如果须要在一段文本里匹配某个特定的字符,而该字符可能呈现、也可能不呈现,?无疑是最佳的抉择。

文本

The URL is http://www.forta.com/, to connect
securely use https://www.forta.com/

正则表达式

https?://[\w./]+

后果

这个模式的结尾局部是https?。? 在这里的含意是:后面的字符 s 要么不呈现,要么最多呈现一次。

在 Windows 上应用模式 \r\n\r\n 去匹配空白行,在 Linux 零碎的正则表达式是\n\n。同时实用于 Windows 和 Linux 零碎的正则表达式应该蕴含一个可选的 \r 和一个必须的 \n。

[\r]?\n[\r]?\n

匹配的反复次数

正则表达式里的 + * ? 解决了许多问题,然而光靠这些还不够。比方:

  • + 和 * 匹配的字符个数没有下限。咱们无奈为它们将匹配的字符个数设定一个最大值。
  • +、* 和? 至多匹配零个或一个字符。咱们无奈为它们将匹配的字符个数另行设定一个最小值。
  • 如果只应用 + 和 *,咱们无奈把它们将匹配的字符个数设定为一个准确的数字。

为了解决这些问题并且对重复性匹配有更多的管制,正则表达式语言提供了一个用来设定 反复次数 的语法。反复次数要用 {} 来给出——把数值写在它们之间。

为反复匹配次数设定一个区间

为反复匹配次数设定一个最小值和最大值,这种区间必须以 {2, 4} 这样的模式给出,含意是起码反复 2 次、最多反复 4 次。

文本

4/8/03
10-6-2004
2/2/2
01-01-01

正则表达式

\d{1,2}[-\/]\d{1,2}[-\/]\d{2,4}

后果

匹配“至多反复多少次”

{3,}示意至多反复 3 次,与之等价的说法是“必须反复 3 次或更屡次”。

避免适度匹配

文本

<B>AK</B> and <B>HI</B>

正则表达式

<[Bb]>.*</[Bb]>

后果

这个正则表达式匹配了所有字符,而不是预期的标签内的内容。为什么会这样?因为 *+都是所谓的 贪心型 元字符,它们在进行匹配时的行为模式是多多益善而不是适可而止的。

在不须要这种“贪心行为”的时候该怎么办?答案是应用这些元字符的“懈怠型”版本。懈怠型元字符的写法很简略,只有给贪心型元字符加上一个 ? 后缀即可。

贪心型元字符 懈怠型元字符
* *?
+ +?
{n,} {n,}?

对于下面的例子,应用正则表达式

<[Bb]>.*?</[Bb]>

后果

小结

正则表达式的真正威力体现在反复次数匹配方面。

  • +:匹配字符的一次或屡次呈现
  • ?:匹配字符的 0 次或一次呈现
  • *:匹配字符的 0 次或屡次呈现
  • {}:准确地设定反复次数

元字符分 贪心型 懈怠型 两种;在须要避免适度匹配的场合下,应用 懈怠型 元字符来结构你的正则表达式。

地位匹配

边界

地位匹配用来解决 在什么中央 进行字符串匹配操作的问题。例如应用 cat 正则搜寻文本,scatter也会被匹配到,如果只是想搜寻 cat 这个单词,就须要边界。

单词边界

单词边界由限定符 \b 指定,匹配一个单词的开始或结尾。\b 匹配的是这样的地位,这个地位位于一个可能用来形成单词的字符(字母、数字和下划线,也就是与 \w 相匹配的字符)和一个不能用来形成单词的字符(\W)之间。

如果不想匹配单词边界,应用\B

文本

The cat scattered his food.

正则表达式

\bcat\b

后果

字符串边界

单词边界能够用来进行与单词无关的地位匹配(单词的结尾、单词的完结、整个单词)。字符串边界有着相似的用处,用来进行与字符串无关的地位匹配(字符串的结尾、字符串的完结、整个字符串)。用来定义字符串边界的元字符有两个:

  • ^:定义字符串结尾
  • $:定义字符串结尾

文本

<?xml version="1.0" ?>
xmlns:blablabla
xmlns:blablabla

正则表达式

^\s*<\?xml.*\?>

后果

小结

正则表达式不仅能够用来匹配任意长度的文本块,还能够用来匹配呈现在字符串中特定地位的文本。\b用来指定一个单词边界(\B 刚好相同)。^$ 用来指定字符串边界(字符串的结尾和完结)。

应用子表达式

什么是子表达式

咱们曾经晓得了如何匹配一个字符的间断多次重复。比方 \d+ 将匹配一个或多个数字字符,而 https? 将匹配 http 或 https。然而这两个用来表明反复次数的元字符 只作用于紧挨着它的前一个字符或元字符

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

文本

Hello, my name is yano 
I like 123  

正则表达式

(){2,}

后果

()是一个子表达式,它是一个独立的元素,紧跟在它前面的 {2,} 将作用于这个子表达式(而不仅仅是分号)。

子表达式的嵌套

子表达式容许嵌套,多重嵌套的子表达式能够结构出性能极其弱小的正则表达式来,然而难免会让模式变得难以浏览和了解。

如何匹配一个IP 地址

一个非法的 IP 地址里的各组数字必须满足:

  • 任何一个 1 位或 2 位数字
  • 任何一个以 1 结尾的 3 位数字
  • 任何一个以 2 结尾、第 2 位数字在 0~5 之间的 3 位数字
  • 任何一个以 25 结尾、第 3 位数字在 0~5 之间的 3 位数字

正则表达式

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

小结

子表达式的作用是把同一个表达式的各个相干局部组合在一起。子表达式必须用 () 来定义。子表达式的常见用处包含:对反复次数元字符的作用对象作出准确的设定和管制、对 | 操作符的 OR 条件作出准确的定义等等。

回溯援用:前后一致匹配

回溯援用有什么用

首先看一个例子。HTML 程序员常常应用题目标签(<H1><H6>,以及配对的完结标签)来定义和排版 Web 页面里的题目文字。假如须要找到某个 Web 页面的所有题目文字,不论它的级别是多少。

文本

<BODY>
<H1>Welcom to my Homepage</H1>
Content is divided into two sections:
<H2>ColdFusion</H2>
Happy Fish
<H2>Wireless</H2>
EXIT
</BODY>

正则表达式

<[hH]1>.*</[hH]1>

后果

模式 <[hH]1>.*</[hH]1> 只能匹配一级题目,然而如何能力匹配任意级别的题目呢?如果应用一个字符汇合来代替 1,如下所示:

正则表达式

<[hH][1-6]>.*?</[hH][1-6]>

后果

这个模式匹配任何一级题目的开始标签和完结标签,然而匹配还是会有问题,如果一个 HTML 的文本有问题,<H2>开始标签对应的完结标签是 </H3> 怎么办?如下所示:

文本

<BODY>
<H1>Welcom to my Homepage</H1>
Content is divided into two sections:
<H2>ColdFusion</H2>
Happy Fish
<H2>Wireless</H3>
EXIT
</BODY>

正则表达式

<[hH][1-6]>.*?</[hH][1-6]>

后果

在这个例子中,原始文本里有一个题目是以 <H2> 结尾、以 <H3> 完结的。这显然是一个不非法的题目,然而它与咱们所应用的模式匹配上了。呈现这种状况的本源是 这个模式的第 2 局部对模式的第 1 局部毫无所知。要想彻底解决这个问题,就只能求助于回溯援用。

回溯援用匹配

对于上述文本,应用正则表达式

<[hH]([1-6])>.*?</[hH]\1>

后果

并没有匹配谬误标签,因为应用了回溯援用。这次用 ()[1-6]括了起来,使它成为了一个 自表达式 。这样咱们就能够用来匹配题目完结标签的</[Hh]\1>\1来援用这个自表达式。自表达式 ([1-6]) 匹配数字 1~6,\1只能匹配与之雷同的数字。这样一来,<H2>Wireless</H3>就不会被匹配到了。

回溯援用在替换操作中的利用

到目前为止,博客介绍的正则表达式都是用来执行搜寻的,即在一段文本里查找特定的内容。然而咱们所编写的绝大多数正则表达式模式也能够用来 搜寻文本 ,然而还能够用来实现各种简单的 替换操作。正则表达式更实用于简单的替换,尤其是须要应用回溯援用的场合。

如果咱们须要把原始文本里的电子邮件地址全都转换为可点击的链接,该怎么办?

文本

Hello, ben@forta.com is my email address.

正则表达式

(\w+[\w.]*@[\w\.]+\.\w+)

替换

<A HREF="mailto:$1">$1</A>

后果

Hello, <A HREF="mailto:ben@forta.com">ben@forta.com</A> is my email address.

替换操作须要用到两个正则表达式:一个用来给出搜寻模式,另一个用来给出匹配文本的替换模式。回溯援用能够 跨模式应用 ,在第一个模式里被匹配的子表达式能够用在第二个模式里。这次正则表达式加了一对(),把它变成了一个子表达式,这样被匹配到的文本就能够用在替换模式里了。<A HREF="mailto:$1">$1</A> 应用了两次被匹配的子表达式($1)。

大小写转换

用来进行大小写转换的元字符

元字符 阐明
\E 完结 \L 或 \U 转换
\S 把下一个字符转换为小写)
\L 把 \L 到 \E 之间的字符全副转换为小写
\u 把下一个字符转换为大写
\U 把 \U 到 \E 之间的字符全副转换为大写

\l 和 \u 只能把 下一个字符(或子表达式)转换为小写或大写。\L 和 \U 将它前面的所有字符转换为小写或大写,直到遇到 \E 为止。

上面将一级题目的题目文字转换为大写:

文本

<BODY>
<H1>Welcom to my Homepage</H1>
Content is divided into two sections:
<H2>ColdFusion</H2>
Happy Fish
<H2>Wireless</H3>
EXIT
</BODY>

正则表达式

(<[Hh]1>)(.*?)(</[Hh]1>)

替换

$1\U$2\E$3

后果

<BODY>
<H1>WELCOM TO MY HOMEPAGE</H1>
Content is divided into two sections:
<H2>ColdFusion</H2>
Happy Fish
<H2>Wireless</H3>
EXIT
</BODY>

小结

子表达式 用来定义字符或表达式的汇合。除了能够用在 反复匹配操作 以外,还能够在 模式的外部 被援用,这种援用被称为 回溯援用 。回溯援用在 文本匹配 文本替换 操作里十分有用。

前后查找

有时候须要正则表达式标记要 匹配的文本的地位(而不仅仅是文本自身)。这就引出了前后查找(lookaround,对某一地位的前后内容进行查找)的概念。

前后查找

咱们当初要把一个 Web 页面的页面题目提取进去。HTML 页面题目是呈现在 <TITLE></TITLE>标签之间的文字。而这对标签又必须嵌在 HTML 代码的 <HEAD> 局部里。

文本

<HEAD>
<TITLE>Ben Forta's Homepage</TITLE>
</HEAD>

正则表达式

<TITLE>.*</TITLE>

后果

然而这个模式的成果不够现实,因为只有 页面题目 才是咱们须要的。咱们当初须要一种模式,它蕴含的匹配自身并不返回,而是用于确定正确的匹配地位,它并不是匹配后果的一部分——前后查找

向前查找

向前查找指定了一个 必须匹配,但不在后果中返回的模式 。向前查找实际上就是一个子表达式,从语法上看,一个向前查找模式其实就是一个以?= 结尾的子表达式,须要匹配的文本跟在 = 前面。

咱们来看一个例子。例子里的原始文本是一些 URL 地址,当初须要把它们的协定名局部提取进去。

文本

http://www.forta.com/
https://mail.forta.com/
ftp://ftp.forta.com/

正则表达式

.+(?=:)

后果

在下面列出的 URL 地址里,协定名和主机名之间以一个 : 分隔。模式 .+ 匹配任意文本(第一个匹配是 http),子表达式 (?=:) 匹配 :。留神,被匹配到的: 并没有呈现在最终的匹配后果里;咱们用 ?= 向正则表达式引擎表明只有找到 : 就行了,不要把它包含在最终的匹配后果里——用术语来讲,就是“不生产”它。

向后查找

  • ?= 向前查找,查找呈现在匹配文本之后的字符,但不生产这个字符
  • ?<= 向后查找,查找呈现在匹配文本之后的字符,但不生产这个字符

把向前查找和向后查找联合起来

文本

<HEAD>
<TITLE>Ben Forta's Homepage</TITLE>
</HEAD>

正则表达式

(?<=<TITLE>).*

后果

(?<=<TITLE>)是一个向后查找操作,匹配但不生产 <TITLE>;(?=</TITLE>) 是一个向前查找操作,匹配但不生产</TITLE>

对前后查找取非

向前查找和向后查找通常用来匹配文本,其目标是为了确定将被返回为匹配后果的文本的地位。这种用法被称为 正前向查找 正后向查找 指的是寻找匹配的事实。

前后查找还有一种不太常见的用法叫 负前后查找 ,指的是不与给定模式相匹配的文本。前后查找必须用! 来取非,替换掉=。各种前后查找操作符如下表所示:

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

小结

有了向后查找,咱们就能够对最终的匹配后果蕴含且只蕴含哪些内容,做出更准确的管制。前后查找操作是咱们能够利用子表达式来指定文本匹配操作的产生地位,并收到只匹配不生产的成果。

公众号

coding 笔记、点滴记录,当前的文章也会同步到公众号(Coding Insight)中,心愿大家关注 ^_^

代码和思维导图在 GitHub 我的项目中,欢送大家 star!


  1. 0-9 ↩
  2. a-zA-Z0-9_ ↩
  3. \f\n\r\t\v ↩
退出移动版