导读

正则表达式是什么?有什么用?

正则表达式(Regular Expression)是一种文本规则,可以用来校验查找替换与规则匹配的文本。

又爱又恨的正则

正则表达式是一个强大的文本匹配工具,但是它的规则实在很繁琐,而且理解起来也颇为蛋疼,容易让人望而生畏。

如何学习正则

刚接触正则时,我看了一堆正则的语义说明,但是仍然不明所以。后来,我多接触一些正则的应用实例,渐渐有了感觉,再结合语义说明,终有领悟。我觉得正则表达式和武侠修练武功差不多,应该先练招式,再练心法。如果一开始就直接看正则的规则,保证你会懵逼。

当你熟悉基本招式(正则基本使用案例)后,也该修炼修炼心法(正则语法)了。真正的高手不能只靠死记硬背那么几招把式。就像张三丰教张无忌太极拳一样,领悟心法,融会贯通,少侠你就可以无招胜有招,成为传说中的绝世高手。

以上闲话可归纳为一句:学习正则应该从实例去理解规则。



打开秘籍:欲练神功,必先自宫!没有蛋,也就不会蛋疼了。

Java正则速成秘籍分三篇:

展示Java对于正则表达式的支持。

介绍正则表达式的语法规则。

从实战出发,介绍正则的常用案例。

Java正则速成秘籍(一)之招式篇 一文,我们学习了Java支持正则功能的API。

本文是Java正则速成秘籍的心法篇。主要介绍正则表达式的语法规则。正则语法规则是一种标准,主流开发语言对于正则语法的支持大体相同。

分组构造、贪婪与懒惰属于正则表达式中较为复杂的应用,建议理解完基本元字符后再去了解。

本文案例中使用的checkMatches、findAll方法请见附录。

本文涉及的所有案例代码,可以在 我的github 找到,如有需要,可以参考。

概述

为了理解下面章节的内容,你需要先了解一些基本概念。

正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

元字符

元字符(metacharacters)就是正则表达式中具有特殊意义的专用字符。

普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

元字符

基本元字符

正则表达式的元字符难以记忆,很大程度上是因为有很多为了简化表达而出现的等价字符。

而实际上最基本的元字符,并没有那么多。对于大部分的场景,基本元字符都可以搞定。

让我们从一个个实例出发,由浅入深的去体会正则的奥妙。

多选 - |

例 匹配一个确定的字符串

checkMatches("abc", "abc");

如果要匹配一个确定的字符串,非常简单,如例1所示。

如果你不确定要匹配的字符串,希望有多个选择,怎么办?

答案是:使用元字符| ,它的含义是或。

例 匹配多个可选的字符串

// 测试正则表达式字符:|
Assert.assertTrue(checkMatches("yes|no", "yes"));
Assert.assertTrue(checkMatches("yes|no", "no"));
Assert.assertFalse(checkMatches("yes|no", "right"));

输出

yes	matches: yes|no
no matches: yes|no
right not matches: yes|no

分组 - ()

如果你希望表达式由多个子表达式组成,你可以使用 ()

例 匹配组合字符串

Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "ended"));
Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "ending"));
Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "playing"));
Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "played"));

输出

ended	matches: (play|end)(ing|ed)
ending matches: (play|end)(ing|ed)
playing matches: (play|end)(ing|ed)
played matches: (play|end)(ing|ed)

指定单字符有效范围 - []

前面展示了如何匹配字符串,但是很多时候你需要精确的匹配一个字符,这时可以使用[]

例 字符在指定范围

// 测试正则表达式字符:[]
Assert.assertTrue(checkMatches("[abc]", "b")); // 字符只能是a、b、c
Assert.assertTrue(checkMatches("[a-z]", "m")); // 字符只能是a - z
Assert.assertTrue(checkMatches("[A-Z]", "O")); // 字符只能是A - Z
Assert.assertTrue(checkMatches("[a-zA-Z]", "K")); // 字符只能是a - z和A - Z
Assert.assertTrue(checkMatches("[a-zA-Z]", "k"));
Assert.assertTrue(checkMatches("[0-9]", "5")); // 字符只能是0 - 9

输出

b	matches: [abc]
m matches: [a-z]
O matches: [A-Z]
K matches: [a-zA-Z]
k matches: [a-zA-Z]
5 matches: [0-9]

指定单字符无效范围 - [^]

例 字符不能在指定范围

如果需要匹配一个字符的逆操作,即字符不能在指定范围,可以使用[^]

// 测试正则表达式字符:[^]
Assert.assertFalse(checkMatches("[^abc]", "b")); // 字符不能是a、b、c
Assert.assertFalse(checkMatches("[^a-z]", "m")); // 字符不能是a - z
Assert.assertFalse(checkMatches("[^A-Z]", "O")); // 字符不能是A - Z
Assert.assertFalse(checkMatches("[^a-zA-Z]", "K")); // 字符不能是a - z和A - Z
Assert.assertFalse(checkMatches("[^a-zA-Z]", "k"));
Assert.assertFalse(checkMatches("[^0-9]", "5")); // 字符不能是0 - 9

输出

b	not matches: [^abc]
m not matches: [^a-z]
O not matches: [^A-Z]
K not matches: [^a-zA-Z]
k not matches: [^a-zA-Z]
5 not matches: [^0-9]

限制字符数量 - {}

如果想要控制字符出现的次数,可以使用{}

字符 描述
{n} n 是一个非负整数。匹配确定的 n 次。
{n,} n 是一个非负整数。至少匹配 n 次。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。

例 限制字符出现次数

// {n}: n 是一个非负整数。匹配确定的 n 次。
checkMatches("ap{1}", "a");
checkMatches("ap{1}", "ap");
checkMatches("ap{1}", "app");
checkMatches("ap{1}", "apppppppppp"); // {n,}: n 是一个非负整数。至少匹配 n 次。
checkMatches("ap{1,}", "a");
checkMatches("ap{1,}", "ap");
checkMatches("ap{1,}", "app");
checkMatches("ap{1,}", "apppppppppp"); // {n,m}: m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。
checkMatches("ap{2,5}", "a");
checkMatches("ap{2,5}", "ap");
checkMatches("ap{2,5}", "app");
checkMatches("ap{2,5}", "apppppppppp");

输出

a	not matches: ap{1}
ap matches: ap{1}
app not matches: ap{1}
apppppppppp not matches: ap{1}
a not matches: ap{1,}
ap matches: ap{1,}
app matches: ap{1,}
apppppppppp matches: ap{1,}
a not matches: ap{2,5}
ap not matches: ap{2,5}
app matches: ap{2,5}
apppppppppp not matches: ap{2,5}

转义字符 - /

如果想要查找元字符本身,你需要使用转义符,使得正则引擎将其视作一个普通字符,而不是一个元字符去处理。

* 的转义字符:\*
+ 的转义字符:\+
? 的转义字符:\?
^ 的转义字符:\^
$ 的转义字符:\$
. 的转义字符:\.

如果是转义符\本身,你也需要使用\\

指定表达式字符串的开始和结尾 - ^、$

如果希望匹配的字符串必须以特定字符串开头,可以使用^

注:请特别留意,这里的^ 一定要和 [^] 中的 “^” 区分。

例 限制字符串头部

Assert.assertTrue(checkMatches("^app[a-z]{0,}", "apple")); // 字符串必须以app开头
Assert.assertFalse(checkMatches("^app[a-z]{0,}", "aplause"));

输出

apple	matches: ^app[a-z]{0,}
aplause not matches: ^app[a-z]{0,}

如果希望匹配的字符串必须以特定字符串开头,可以使用$

例 限制字符串尾部

Assert.assertTrue(checkMatches("[a-z]{0,}ing$", "playing")); // 字符串必须以ing结尾
Assert.assertFalse(checkMatches("[a-z]{0,}ing$", "long"));

输出

playing	matches: [a-z]{0,}ing$
long not matches: [a-z]{0,}ing$

等价字符

等价字符,顾名思义,就是对于基本元字符表达的一种简化(等价字符的功能都可以通过基本元字符来实现)。

在没有掌握基本元字符之前,可以先不用理会,因为很容易把人绕晕。

等价字符的好处在于简化了基本元字符的写法。

表示某一类型字符的等价字符

下表中的等价字符都表示某一类型的字符。

字符 描述
. 匹配除“\n”之外的任何单个字符。
\d 匹配一个数字字符。等价于[0-9]。
\D 匹配一个非数字字符。等价于[^0-9]。
\w 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的单词字符指的是Unicode字符集。
\W 匹配任何非单词字符。
\s 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S 匹配任何可见字符。等价于[ \f\n\r\t\v]。

案例 基本等价字符的用法

// 匹配除“\n”之外的任何单个字符
Assert.assertTrue(checkMatches(".{1,}", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"));
Assert.assertTrue(checkMatches(".{1,}", "~!@#$%^&*()+`-=[]{};:<>,./?|\\"));
Assert.assertFalse(checkMatches(".", "\n"));
Assert.assertFalse(checkMatches("[^\n]", "\n")); // 匹配一个数字字符。等价于[0-9]
Assert.assertTrue(checkMatches("\\d{1,}", "0123456789"));
// 匹配一个非数字字符。等价于[^0-9]
Assert.assertFalse(checkMatches("\\D{1,}", "0123456789")); // 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的单词字符指的是Unicode字符集
Assert.assertTrue(checkMatches("\\w{1,}", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"));
Assert.assertFalse(checkMatches("\\w{1,}", "~!@#$%^&*()+`-=[]{};:<>,./?|\\"));
// 匹配任何非单词字符
Assert.assertFalse(checkMatches("\\W{1,}", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"));
Assert.assertTrue(checkMatches("\\W{1,}", "~!@#$%^&*()+`-=[]{};:<>,./?|\\")); // 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]
Assert.assertTrue(checkMatches("\\s{1,}", " \f\r\n\t"));
// 匹配任何可见字符。等价于[^ \f\n\r\t\v]
Assert.assertFalse(checkMatches("\\S{1,}", " \f\r\n\t"));

输出

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_	matches: .{1,}
~!@#$%^&*()+`-=[]{};:<>,./?|\\ matches: .{1,}
\n not matches: .
\n not matches: [^\n]
0123456789 matches: \\d{1,}
0123456789 not matches: \\D{1,}
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ matches: \\w{1,}
~!@#$%^&*()+`-=[]{};:<>,./?|\\ not matches: \\w{1,}
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ not matches: \\W{1,}
~!@#$%^&*()+`-=[]{};:<>,./?|\\ matches: \\W{1,}
\f\r\n\t matches: \\s{1,}
\f\r\n\t not matches: \\S{1,}

限制字符数量的等价字符

在基本元字符章节中,已经介绍了限制字符数量的基本元字符 - {}

此外,还有 *+? 这个三个为了简化写法而出现的等价字符,我们来认识一下。

字符 描述
* 匹配前面的子表达式零次或多次。等价于{0,}。
+ 匹配前面的子表达式一次或多次。等价于{1,}。
? 匹配前面的子表达式零次或一次。等价于 {0,1}。

案例 限制字符数量的等价字符

// *: 匹配前面的子表达式零次或多次。* 等价于{0,}。
checkMatches("ap*", "a");
checkMatches("ap*", "ap");
checkMatches("ap*", "app");
checkMatches("ap*", "apppppppppp"); // +: 匹配前面的子表达式一次或多次。+ 等价于 {1,}。
checkMatches("ap+", "a");
checkMatches("ap+", "ap");
checkMatches("ap+", "app");
checkMatches("ap+", "apppppppppp"); // ?: 匹配前面的子表达式零次或一次。? 等价于 {0,1}。
checkMatches("ap?", "a");
checkMatches("ap?", "ap");
checkMatches("ap?", "app");
checkMatches("ap?", "apppppppppp");

输出

a	matches: ap*
ap matches: ap*
app matches: ap*
apppppppppp matches: ap*
a not matches: ap+
ap matches: ap+
app matches: ap+
apppppppppp matches: ap+
a matches: ap?
ap matches: ap?
app not matches: ap?
apppppppppp not matches: ap?

元字符优先级顺序

正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。

下表从最高到最低说明了各种正则表达式运算符的优先级顺序:

运算符 说明
\ 转义符
(), (?

Java正则速成秘籍(二)之心法篇的更多相关文章

  1. Java正则速成秘籍(一)之招式篇

    导读 正则表达式是什么?有什么用? 正则表达式(Regular Expression)是一种文本规则,可以用来校验.查找.替换与规则匹配的文本. 又爱又恨的正则 正则表达式是一个强大的文本匹配工具,但 ...

  2. Java正则速成秘籍(三)之见招拆招篇

    导读 正则表达式是什么?有什么用? 正则表达式(Regular Expression)是一种文本规则,可以用来校验.查找.替换与规则匹配的文本. 又爱又恨的正则 正则表达式是一个强大的文本匹配工具,但 ...

  3. Java 面试知识点解析(二)——高并发编程篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  4. Java多线程编程实战指南(核心篇)读书笔记(二)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  5. Java 面试知识点解析(四)——版本特性篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  6. 【面试题】2018年最全Java面试通关秘籍汇总集!

    [面试题]2018年最全Java面试通关秘籍汇总集!(转载于互联网)   前几天在交流群里有些小伙伴问面试相关的试题,当时给出了一些问题,苦于打字太累就没写下去了,但觉得这是一个很不负责任的表现,于是 ...

  7. 【JAVA编码】 JAVA字符编码系列二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换

    http://blog.csdn.net/qinysong/article/details/1179489 这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记 ...

  8. 【Java学习笔记之二十六】深入理解Java匿名内部类

    在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意 ...

  9. JAVA之旅(二十九)——文件递归,File结束练习,Properties,Properties存取配置文件,load,Properties的小练习

    JAVA之旅(二十九)--文件递归,File结束练习,Properties,Properties存取配置文件,load,Properties的小练习 我们继续学习File 一.文件递归 我们可以来实现 ...

随机推荐

  1. C++ 11 多线程--线程管理

    说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...

  2. 根据ip判断返回城市名称查询当地天气

    <?phpheader("content-type:text/html;charset=utf-8");date_default_timezone_set("Asi ...

  3. 当我们在谈论kmeans(1)

    本稿为初稿,后续可能还会修改:如果转载,请务必保留源地址,非常感谢! 博客园:http://www.cnblogs.com/data-miner/ 简书:建设中... 知乎:建设中... 当我们在谈论 ...

  4. AOP之Castle DynamicProxy 动态代理

    这里主要介绍使用castle这个动态代理,在.net一些开源的框架里可以找到它的影子,就连微软的rchard也是使用这个进行方法拦截等可以基于这个进行方法拦截,在这个方面PostSharp算是比较好用 ...

  5. MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN

    在Membership系列的最后一篇引入了ASP.NET Identity,看到大家对它还是挺感兴趣的,于是来一篇详解登录原理的文章.本文会涉及到Claims-based(基于声明)的认证,我们会详细 ...

  6. 淘宝UWP中的100个为什么

    从淘宝UWP第一版发布到现在,已经有十个月了,期间收到了用户各种各样的反馈,感谢这些用户的反馈,指导我们不断的修正.完善应用.但是也有一部分需求或建议,由于资源或技术的限制,目前确实无法做到,只能对广 ...

  7. MVVM大比拼之avalon.js源码精析

    简介 avalon是国内 司徒正美 写的MVVM框架,相比同类框架它的特点是: 使用 observe 模式,性能高. 将原始对象用object.defineProperty重写,不需要用户像用knoc ...

  8. 高性能Javascript--高效的数据访问

    接上一篇,希望能写一个高性能Javascript专题. 第一篇:高性能Javascript--脚本的无阻塞加载策略. 参考摘录<高性能Javascript>. 经典计算机科学的一个问题是, ...

  9. C# 开发windows服务的一些心得

    最近在做一个windows服务的项目,发现并解决了一些问题,拿出来和大家分享一下,以下windows服务简称“服务” 文章会在适合时间更新,因为朋友们在不断提出新的意见或思路,感谢-.- 1.服务如何 ...

  10. Atitit 2016年attilax事业成就表

    Atitit 2016年attilax事业成就表 1.1. 项目管理模型---Rem模型2 1.2. 项目管理模型---vsi模型val specs implt3 1.3. 研发体系完善(gui与游戏 ...