Java正则速成秘籍(一)之招式篇
导读
正则表达式是什么?有什么用?
正则表达式(Regular Expression)是一种文本规则,可以用来校验、查找、替换与规则匹配的文本。
又爱又恨的正则
正则表达式是一个强大的文本匹配工具,但是它的规则实在很繁琐,而且理解起来也颇为蛋疼,容易让人望而生畏。
如何学习正则
刚接触正则时,我看了一堆正则的语义说明,但是仍然不明所以。后来,我多接触一些正则的应用实例,渐渐有了感觉,再结合语义说明,终有领悟。我觉得正则表达式和武侠修练武功差不多,应该先练招式,再练心法。如果一开始就直接看正则的规则,保证你会懵逼。
当你熟悉基本招式(正则基本使用案例)后,也该修炼修炼心法(正则语法)了。真正的高手不能只靠死记硬背那么几招把式。就像张三丰教张无忌太极拳一样,领悟心法,融会贯通,少侠你就可以无招胜有招,成为传说中的绝世高手。
以上闲话可归纳为一句:学习正则应该从实例去理解规则。
打开秘籍:欲练神功,必先自宫!没有蛋,也就不会蛋疼了。
Java正则速成秘籍分三篇:
展示Java对于正则表达式的支持。
介绍正则表达式的语法规则。
从实战出发,介绍正则的常用案例。
本文是Java正则速成秘籍的招式篇。主要介绍JDK对于正则表达式的支持。
概述
JDK中的java.util.regex
包提供了对正则表达式的支持。
java.util.regex
有三个核心类:
- Pattern类:
Pattern
是一个正则表达式的编译表示。 - Matcher类:
Matcher
是对输入字符串进行解释和匹配操作的引擎。 - PatternSyntaxException:
PatternSyntaxException
是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
注:需要格外注意一点,在Java中使用反斜杠"\"时必须写成 "\\"
。所以本文的代码出现形如String regex = "\\$\\{.*?\\}"
其实就是"\$\{.*?\}",不要以为是画风不对哦。
Pattern类
Pattern
类没有公共构造方法。要创建一个Pattern
对象,你必须首先调用其静态方法compile
,加载正则规则字符串,然后返回一个Pattern对象。
与Pattern
类一样,Matcher
类也没有公共构造方法。你需要调用Pattern
对象的matcher
方法来获得一个Matcher
对象。
案例:Pattern和Matcher的初始化
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
Matcher类
Matcher
类可以说是java.util.regex
核心类中的必杀技!
Matcher
类有三板斧(三类功能):
- 校验
- 查找
- 替换
下面我们来领略一下这三块的功能。
校验文本是否与正则规则匹配
为了检查文本是否与正则规则匹配,Matcher提供了以下几个返回值为boolean
的方法。
序号 | 方法及说明 |
---|---|
1 | **public boolean lookingAt() ** 尝试将从区域开头开始的输入序列与该模式匹配。 |
2 | **public boolean find() **尝试查找与该模式匹配的输入序列的下一个子序列。 |
3 | public boolean find(int start)重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。 |
4 | **public boolean matches() **尝试将整个区域与模式匹配。 |
如果你傻傻分不清上面的查找方法有什么区别,那么下面一个例子就可以让你秒懂。
案例:lookingAt vs find vs matches
public static void main(String[] args) {
checkLookingAt("hello", "helloworld");
checkLookingAt("world", "helloworld");
checkFind("hello", "helloworld");
checkFind("world", "helloworld");
checkMatches("hello", "helloworld");
checkMatches("world", "helloworld");
checkMatches("helloworld", "helloworld");
}
private static void checkLookingAt(String regex, String content) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
if (m.lookingAt()) {
System.out.println(content + "\tlookingAt: " + regex);
} else {
System.out.println(content + "\tnot lookingAt: " + regex);
}
}
private static void checkFind(String regex, String content) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
if (m.find()) {
System.out.println(content + "\tfind: " + regex);
} else {
System.out.println(content + "\tnot find: " + regex);
}
}
private static void checkMatches(String regex, String content) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
if (m.matches()) {
System.out.println(content + "\tmatches: " + regex);
} else {
System.out.println(content + "\tnot matches: " + regex);
}
}
输出
helloworld lookingAt: hello
helloworld not lookingAt: world
helloworld find: hello
helloworld find: world
helloworld not matches: hello
helloworld not matches: world
helloworld matches: helloworld
说明
regex = “world” 表示的正则规则是以world开头的字符串,regex = “hello” 和regex = “helloworld” 也是同理。
lookingAt
方法从头部开始,检查content字符串是否有子字符串于正则规则匹配。find
方法检查content字符串是否有子字符串于正则规则匹配,不管字符串所在位置。matches
方法检查content字符串整体是否与正则规则匹配。
查找匹配正则规则的文本位置
为了查找文本匹配正则规则的位置,Matcher
提供了以下方法:
序号 | 方法及说明 |
---|---|
1 | **public int start() **返回以前匹配的初始索引。 |
2 | public int start(int group) 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引 |
3 | public int end()返回最后匹配字符之后的偏移量。 |
4 | public int end(int group)返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
5 | public String group()返回前一个符合匹配条件的子序列。 |
6 | public String group(int group)返回指定的符合匹配条件的子序列。 |
案例:使用start()、end()、group() 查找所有匹配正则条件的子序列
public static void main(String[] args) {
final String regex = "world";
final String content = "helloworld helloworld";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
System.out.println("content: " + content);
int i = 0;
while (m.find()) {
i++;
System.out.println("[" + i + "th] found");
System.out.print("start: " + m.start() + ", ");
System.out.print("end: " + m.end() + ", ");
System.out.print("group: " + m.group() + "\n");
}
}
输出
content: helloworld helloworld
[1th] found
start: 5, end: 10, group: world
[2th] found
start: 16, end: 21, group: world
说明
例子很直白,不言自明了吧。
替换匹配正则规则的文本
替换方法是替换输入字符串里文本的方法:
序号 | 方法及说明 |
---|---|
1 | public Matcher appendReplacement(StringBuffer sb, String replacement)实现非终端添加和替换步骤。 |
2 | public StringBuffer appendTail(StringBuffer sb)实现终端添加和替换步骤。 |
3 | **public String replaceAll(String replacement) ** 替换模式与给定替换字符串相匹配的输入序列的每个子序列。 |
4 | public String replaceFirst(String replacement) 替换模式与给定替换字符串匹配的输入序列的第一个子序列。 |
5 | public static String quoteReplacement(String s)返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。 |
案例:replaceFirst vs replaceAll
public static void main(String[] args) {
String regex = "can";
String replace = "can not";
String content = "I can because I think I can.";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
System.out.println("content: " + content);
System.out.println("replaceFirst: " + m.replaceFirst(replace));
System.out.println("replaceAll: " + m.replaceAll(replace));
}
输出
content: I can because I think I can.
replaceFirst: I can not because I think I can.
replaceAll: I can not because I think I can not.
说明
replaceFirst:替换第一个匹配正则规则的子序列。
replaceAll:替换所有匹配正则规则的子序列。
案例:appendReplacement、appendTail和replaceAll
public static void main(String[] args) {
String regex = "can";
String replace = "can not";
String content = "I can because I think I can.";
StringBuffer sb = new StringBuffer();
StringBuffer sb2 = new StringBuffer();
System.out.println("content: " + content);
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
while (m.find()) {
m.appendReplacement(sb, replace);
}
System.out.println("appendReplacement: " + sb);
m.appendTail(sb);
System.out.println("appendTail: " + sb);
}
输出
content: I can because I think I can.
appendReplacement: I can not because I think I can not
appendTail: I can not because I think I can not.
说明
从输出结果可以看出,appendReplacement
和appendTail
方法组合起来用,功能和replaceAll
是一样的。
如果你查看replaceAll
的源码,会发现其内部就是使用appendReplacement
和appendTail
方法组合来实现的。
案例:quoteReplacement和replaceAll,解决特殊字符替换问题
public static void main(String[] args) {
String regex = "\\$\\{.*?\\}";
String replace = "${product}";
String content = "product is ${productName}.";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
String replaceAll = m.replaceAll(replace);
System.out.println("content: " + content);
System.out.println("replaceAll: " + replaceAll);
}
输出
Exception in thread "main" java.lang.IllegalArgumentException: No group with name {product}
at java.util.regex.Matcher.appendReplacement(Matcher.java:849)
at java.util.regex.Matcher.replaceAll(Matcher.java:955)
at org.zp.notes.javase.regex.RegexDemo.wrongMethod(RegexDemo.java:42)
at org.zp.notes.javase.regex.RegexDemo.main(RegexDemo.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
说明
String regex = "\\$\\{.*?\\}";
表示匹配类似${name}
这样的字符串。由于$
、{
、}
都是特殊字符,需要用反义字符\
来修饰才能被当做一个字符串字符来处理。
上面的例子是想将 ${productName}
替换为 ${product}
,然而replaceAll
方法却将传入的字符串中的$
当做特殊字符来处理了。结果产生异常。
如何解决这个问题?
JDK1.5引入了quoteReplacement
方法。它可以用来转换特殊字符。其实源码非常简单,就是判断字符串中如果有\
或$
,就为它加一个转义字符\
我们对上面的代码略作调整:
m.replaceAll(replace)
改为m.replaceAll(Matcher.quoteReplacement(replace))
,新代码如下:
public static void main(String[] args) {
String regex = "\\$\\{.*?\\}";
String replace = "${product}";
String content = "product is ${productName}.";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
String replaceAll = m.replaceAll(Matcher.quoteReplacement(replace));
System.out.println("content: " + content);
System.out.println("replaceAll: " + replaceAll);
}
输出
content: product is ${productName}.
replaceAll: product is ${product}.
说明
字符串中如果有\
或$
,不能被正常解析的问题解决。
Java正则速成秘籍(一)之招式篇的更多相关文章
- Java正则速成秘籍(三)之见招拆招篇
导读 正则表达式是什么?有什么用? 正则表达式(Regular Expression)是一种文本规则,可以用来校验.查找.替换与规则匹配的文本. 又爱又恨的正则 正则表达式是一个强大的文本匹配工具,但 ...
- Java正则速成秘籍(二)之心法篇
导读 正则表达式是什么?有什么用? 正则表达式(Regular Expression)是一种文本规则,可以用来校验.查找.替换与规则匹配的文本. 又爱又恨的正则 正则表达式是一个强大的文本匹配工具,但 ...
- Netty基础招式——ChannelHandler的最佳实践
本文是Netty系列第7篇 上一篇文章我们深入学习了Netty逻辑架构中的核心组件EventLoop和EventLoopGroup,掌握了Netty的线程模型,并且介绍了Netty4线程模型中的无锁串 ...
- Netty常用招式——ChannelHandler与编解码
本文是Netty系列第8篇 上一篇文章我们深入学习了Netty逻辑架构中的核心组件ChannelHandler和ChannelPipeline,并介绍了它在日常开发使用中的最佳实践.文中也提到了,Ch ...
- java 正则匹配空格字符串 正则表达式截取字符串
java 正则匹配空格字符串 正则表达式截取字符串 需求:从一堆sql中取出某些特定字符串: 比如配置的sql语句为:"company_code = @cc and project_id = ...
- url 中非法字符替换,java 正则替换
url在传输时不允许的一些字符串,参考自:http://www.ietf.org/rfc/rfc1738.txt 以下字符用java正则替换为"_",一句话搞定: "{& ...
- 解密DNSPOD应对DDoS攻击招式!
最近,安全专家Incapsula在最新版<DDoS威胁环境报告>指出.现在实施DDoS攻击的人仅仅有两类:一类是专业网络黑客.而还有一类就是所谓的botter. 简言之,booter就是僵 ...
- 【面试题】2018年最全Java面试通关秘籍汇总集!
[面试题]2018年最全Java面试通关秘籍汇总集!(转载于互联网) 前几天在交流群里有些小伙伴问面试相关的试题,当时给出了一些问题,苦于打字太累就没写下去了,但觉得这是一个很不负责任的表现,于是 ...
- 通用且常用的Java正则匹配工具,用以检查邮箱名、电话号码、用户密码、邮政编码等合法性
一个通用且常用的Java正则匹配工具,用以检查邮箱名.电话号码.用户密码.邮政编码等合法性. import java.util.regex.Matcher; import java.util.rege ...
随机推荐
- .NET Core 系列5 :使用 Nuget打包类库
NuGet是个开源项目,项目包括 NuGet VS插件/NuGet Explorer/NuGetServer/NuGet命令行等项目,.NET Core项目完全使用Nuget 管理组件之间的依赖关系, ...
- ZIP压缩算法详细分析及解压实例解释
最近自己实现了一个ZIP压缩数据的解压程序,觉得有必要把ZIP压缩格式进行一下详细总结,数据压缩是一门通信原理和计算机科学都会涉及到的学科,在通信原理中,一般称为信源编码,在计算机科学里,一般称为数据 ...
- dagger2系列之依赖方式dependencies、包含方式(从属方式)SubComponent
本篇是实战文章,从代码的角度分析这两种方式.本文参考自下列文章: http://www.jianshu.com/p/1d42d2e6f4a5 http://www.jianshu.com/p/94d4 ...
- BlockingCollection使用
BlockingCollection是一个线程安全的生产者-消费者集合. 代码 public class BlockingTest { BlockingCollection<int> bc ...
- maven依赖查询地址
http://search.maven.org/#search%7Cga%7C1%7C
- ABP领域层
1.实体Entites 1.1 概念 实体是DDD(领域驱动设计)的核心概念之一. 实体是具有唯一标识的ID且存储在数据库总.实体通常被映射成数据库中的一个表. 在ABP中,实体继承自Entity类. ...
- xss和sql注入原理学习
8.4 Web跨站脚本攻击 8.4.1 跨站脚本攻击的原理(1) 跨站脚本在英文中称为Cross-Site Scripting,缩写为CSS.但是,由于层叠样式表 (Cascading Style ...
- Android 解析XML文件和生成XML文件
解析XML文件 public static void initXML(Context context) { //can't create in /data/media/0 because permis ...
- 安卓客户端a标签长按弹框提示解决办法
昨天工作时候发现一个bug,是关于a标签的,在安卓客户端中,如果是a标签的话,长按会出现一个弹框,如图所示 是因为安卓客户端的长按触发机制,以后进行wap端开发的时候,如果用到跳转页面尽量不要用a标签 ...
- 张高兴的 UWP 开发笔记:汉堡菜单进阶
不同于Windows 8应用,Windows 10引入了"汉堡菜单"这一导航模式.说具体点,就拿官方的天气应用来说,左上角三条横杠的图标外加一个SplitView控件组成的这一导航 ...