- 提取文章中所有的英文单词
- 提取文章中所有的数字
- 提取文章中所有的英文单词和数字
- 给你一个字符串(或文章),请你找出所有四个数字连在一起的字串
- 请验证输入的邮件,是否符合电子邮件格式
- 请验证输入的手机号,是否符合手机号格式
结论:正则表达式是处理文本的利器
-
一个正则表达式,就是用某种模式去匹配字符串的一个公式。很多人因为正则表达式看上去比较古怪而且复杂所以不敢去使用。但是当你看完这篇文章并去加以练习后就会觉得这些复杂的表达式写起来还是相当简单的,而且,一旦你弄懂它们,你就能把数小时辛苦而且易错的文本处理工作缩短在几分钟(甚至几秒钟)内完成。
-
正则表达式:regular expression => RegExp
简单的说:正则表达式是对字符串执行模式匹配的技术。
1.提取文章中所有的英文单词
运行结果:
代码解读:
- 先创建一个 Pattern 对象 , 模式对象, 可以理解成就是一个正则表达式对象
- 创建一个匹配器对象
就是 matcher 匹配器按照 pattern (模式/样式), 到 content 文本中去匹配,找到就返回 true, 否则就返回 false
- 可以开始循环匹配
关于 matcher.find() 和 matcher.group() 涉及到源码,此处的源码挺简单,建议阅读
会了这一个例子之后,掌握了正则表达式的三个步骤,做其它的匹配都是大同小异的,下面再举几个例子来巩固一下。
2.提取文章中所有的数字
注意,其它的代码都和上面的代码完全一样,只是正则表达式变了而已
运行结果:
3.提取文章中所有的英文单词和数字
运行结果:
4.提取 IP 地址
\d 表示一个任意的数字
运行结果:
主要就是研究 matcher.find() 和 matcher.group()
通过一个例子来说明,即找到字符串里的连续4个数字
强烈建议:自己进行 debug,在第 16 行的位置加一个断点。用文字来叙述的话是不太好描述的,建议大家看完之后,自己进行 debug
代码是如何找到 1998 的?
matcher.find() 完成的任务:
- 根据指定的规则 ,定位满足规则的子字符串(比如 1998 )
- 找到后,将子字符串的开始的索引记录到 matcher 对象的属性 int[] groups 中,即记录 groups[0] = 0(因为 1998 中 1 的索引为 0),把该子字符串的结束的索引 + 1 的值记录到 groups[1] = 4。(为什么要记录索引 + 1呢?因为下面 matcher.group() 要调用 getSubSequence 进行截取字符串)
- 同时记录 oldLast 的值为 子字符串的结束的 索引 + 1 的值即 4, 即下次执行 find 时,就从 4 开始匹配
matcher.group(0) 分析:
首先看一下源码
这里我们只需要看第 8 行代码即可,注意我们传的参数的值是 0(因为 matcher.group(0)),再看第 8 行代码,很容易计算出 groups[group * 2] = groups[0] = 0,groups[group * 2 + 1] = groups[1] = 4
根据 groups[0] = 0 和 groups[1] = 4 的记录的位置,从 content 开始截取子字符串返回,就是 [0,4) 包含 0 但是不包含索引为 4 的位置(getSubSequence 的作用),再通过 toString() 返回,即 1998。
执行下一次循环,即先执行 matcher.find(),再执行 matcher.find() 。由于设定的字符串匹配规则,此时会定位到 1999 的位置。再次按照上面的规则来执行
-
根据指定的规则 ,定位满足规则的子字符串(比如 1999 )
-
找到后,将子字符串的开始的索引记录到 matcher 对象的属性 int[] groups;
groups[0] = 31,把该子字符串的结束的索引 + 1 的值记录到 groups[1] = 35。
-
同时记录 oldLast 的值为 子字符串的结束的 索引+1的值即 35, 即下次执行 find 时,就从 35 开始匹配
再次执行 matcher.group(0)
由于传的参还是 0,故由
可知:groups[group * 2] = groups[0] = 31,groups[group * 2 + 1] = groups[1] = 35,故截取 [31,35)的字符串返回,即 1999
如果你有感觉到如果,不调用 matcher.group(0),而是调用 matcher.group(1),或者 matcher.group(2) 呢,那么说明你对正则表达式的理解更上一层了
什么是分组,比如 (dd)(dd) ,正则表达式中有() 表示分组,第1个()表示第1组,第2个()表示第2组…
matcher.find() 完成的任务 (考虑分组)
-
根据指定的规则 ,定位满足规则的子字符串(比如(19)(98))
-
找到后,将 子字符串的开始的索引记录到 matcher 对象的属性 int[] groups 中,
2.1 groups[0] = 0 , 把该子字符串的结束的索引+1的值记录到 groups[1] = 4
2.2 记录1组()匹配到的字符串 groups[2] = 0 groups[3] = 2
2.3 记录2组()匹配到的字符串 groups[4] = 2 groups[5] = 4
2.4.如果有更多的分组…
- 同时记录 oldLast 的值为 子字符串的结束的 索引+1的值即 4, 即下次执行 find 时,就从 4 开始匹配
matcher.group():
调用 matcher.group(0) 时:由上面对 matcher.find() 的分析可知:groups[group * 2] = 0,groups[group * 2 + 1] = 4,由 getSubSequence 进行截取,,即返回 1998
调用 matcher.group(1) 时:由上面对 matcher.find() 的分析可知:groups[group * 2] = 0,groups[group * 2 + 1] = 2,由 getSubSequence 进行截取,,即返回 19
调用 matcher.group(2) 时:由上面对 matcher.find() 的分析可知:groups[group * 2] = 2,groups[group * 2 + 1] = 4,由 getSubSequence 进行截取,,即返回 98
运行结果:
小结:
如果正则表达式有 () 即分组,取出匹配的字符串规则如下:
- group(0) 表示匹配到的子字符串
- group(1) 表示匹配到的子字符串的第一组字串
- group(2) 表示匹配到的子字符串的第2组字串
- … 但是分组的数不能越界,比如只有两个分组,但是却调用 matcher.group(3)
元字符
如果想要灵活的运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为:
- 限定符 ---- 限定字符的个数
- 选择匹配符 ----
- 分组组合和反向引用符
- 特殊字符
- 字符匹配符
- 定位符
元字符-转义号 \
\ 符号
说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错。
需要用到转移字符的有:.*+()$/?[]^{}
元字符-字符匹配符
表中出现的 ?,*,+ 等在元字符-限定符中有解释
元字符-选择匹配符
在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个,这时需要用到选择匹配符号
结果:
元字符-限定符
用于指定其前面的字符和组合项连续出现多少次
注意:Java 匹配默认贪婪匹配,即尽可能匹配多的
元字符-定位符
定位符,规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置,这个也是相当有用的,必须掌握。
应用实例
手机号码
要求: 必须以 13,14,15,18 开头的 11 位数 , 比如 13588889999
url 地址
思路:
- 先确定 url 的开始部分 https:// | http:
- .然后通过 ([\w-]+.)+[\w-] 匹配
头的 11 位数 , 比如 13588889999
url 地址
思路:
- 先确定 url 的开始部分 https:// | http:
- .然后通过 ([\w-]+.)+[\w-] 匹配