Skip to content

Commit 937d56d

Browse files
committed
updates
1 parent 9522cb9 commit 937d56d

File tree

7 files changed

+207
-41
lines changed

7 files changed

+207
-41
lines changed

第4章 常见对象/img/regex.png

3.71 KB
Loading

第4章 常见对象/img/regex1.png

11.4 KB
Loading

第4章 常见对象/img/regex2.png

23.2 KB
Loading
Loading

第4章 常见对象/常用正则表达式.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## 常用正则表达式
1+
### [常用正则表达式](https://deerchao.net/tutorials/regex/common.htm)
2+
3+
说明:正则表达式通常用于两种任务:1.验证,2.搜索/替换。用于验证时,通常需要在前后分别加上\^\$,以匹配整个待验证字符串;搜索/替换时是否加上此限定则根据搜索的要求而定,此外,也有可能要在前后加上\b而不是\^\$。此表所列的常用正则表达式,除个别外均未在前后加上任何限定,请根据需要,自行处理。
4+
5+
![](img/常用正则表达式.png)
26

37
### 校验数字的表达式
48
```

第4章 常见对象/正则表达式基础.md

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
# 1. 正则表达式概述
88

9+
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
10+
911
正则表达式,又称正规表示法、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本
1012

1113
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen
@@ -16,7 +18,7 @@
1618
- 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”)
1719
- 可以通过正则表达式,从字符串中获取我们想要的特定部分
1820

19-
## 1.2 正则表达式的特点是
21+
## 1.2 正则表达式的特点
2022

2123
- 灵活性、逻辑性和功能性非常的强;
2224
- 可以迅速地用极简单的方式达到字符串的复杂控制。
@@ -116,7 +118,10 @@
116118
| \Z | 输入的结尾,仅用于最后的结束符(如果有的话) |
117119
| \z | 输入的结尾 |
118120

119-
### 2.4.1 Greedy数量词
121+
### 2.4.1 贪婪与懒惰数量词
122+
123+
- 贪婪数量词
124+
120125
| 符号 | 说明 |
121126
| ------ | ----- |
122127
| X? | 0次或1次 |
@@ -126,6 +131,24 @@
126131
| X{n,} | 至少n次 |
127132
| X{n,m} | n-m次 |
128133

134+
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。
135+
136+
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。在贪婪数量词后面加上?就是懒惰匹配。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:
137+
138+
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。
139+
140+
为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:**最先开始的匹配拥有最高的优先权——The match that begins earliest wins**
141+
142+
- 懒惰限定符
143+
144+
| 代码/语法 | 说明 |
145+
| ------ | ---------------- |
146+
| *? | 重复任意次,但尽可能少重复 |
147+
| +? | 重复1次或更多次,但尽可能少重复 |
148+
| ?? | 重复0次或1次,但尽可能少重复 |
149+
| {n,m}? | 重复n到m次,但尽可能少重复 |
150+
| {n,}? | 重复n次以上,但尽可能少重复 |
151+
129152
### 2.4.2 分组和捕获
130153

131154
分组可以分为两种形式,捕获组和非捕获组。
@@ -191,39 +214,39 @@ Back引用(\n)是说在后面的表达式中我们可以使用组的编号
191214
| ?<= | 后置约束-存在 |
192215
| ?<! | 后置约束-排除 |
193216

194-
### `?=...` 前置约束(存在)
217+
### `?=exp`前置约束(存在)
195218

196-
`?=...` 前置约束(存在), 表示第一部分表达式必须跟在 `?=...`定义的表达式之后.
219+
?=exp 前置约束(存在), 表示第一部分表达式必须跟在 ?=exp 定义的表达式之后.
197220

198-
返回结果只瞒住第一部分表达式. 定义一个前置约束(存在)要使用 `()`. 在括号内部使用一个问号和等号: `(?=...)`.
221+
返回结果只瞒住第一部分表达式. 定义一个前置约束(存在)要使用 `()`. 在括号内部使用一个问号和等号: (?=exp).
199222

200223
前置约束的内容写在括号中的等号后面. 例如, 表达式 `[T|t]he(?=\sfat)` 匹配 `The``the`, 在括号中我们又定义了前置约束(存在) `(?=\sfat)` ,即 `The``the` 后面紧跟着 `(空格)fat`.
201224

202225
```
203226
"[T|t]he(?=\sfat)" => The fat cat sat on the mat.
204227
```
205228

206-
### `?!...` 前置约束-排除
229+
### `?!exp`前置约束-排除
207230

208-
前置约束-排除 `?!` 用于筛选所有匹配结果, 筛选条件为 其后不跟随着定义的格式 `前置约束-排除` 定义和 `前置约束(存在)` 一样, 区别就是 `=` 替换成 `!` 也就是 `(?!...)`.
231+
前置约束-排除 `?!` 用于筛选所有匹配结果, 筛选条件为 其后不跟随着定义的格式 `前置约束-排除` 定义和 `前置约束(存在)` 一样, 区别就是 `=` 替换成 `!` 也就是 (?!exp).
209232

210233
表达式 `[T|t]he(?!\sfat)` 匹配 `The``the`, 且其后不跟着 `(空格)fat`.
211234

212235
```
213236
"[T|t]he(?!\sfat)" => The fat cat sat on the mat.
214237
```
215238

216-
### `?<= ...` 后置约束-存在
239+
### `?<=exp`后置约束-存在
217240

218-
后置约束-存在 记作`(?<=...)` 用于筛选所有匹配结果, 筛选条件为 其前跟随着定义的格式. 例如, 表达式 `(?<=[T|t]he\s)(fat|mat)` 匹配 `fat``mat`, 且其前跟着 `The``the`.
241+
后置约束-存在 记作(?<=exp)用于筛选所有匹配结果, 筛选条件为 其前跟随着定义的格式. 例如, 表达式 `(?<=[T|t]he\s)(fat|mat)` 匹配 `fat``mat`, 且其前跟着 `The``the`.
219242

220243
```
221244
"(?<=[T|t]he\s)(fat|mat)" => The fat cat sat on the mat.
222245
```
223246

224-
### `?<!...` 后置约束-排除
247+
### `?<!exp`后置约束-排除
225248

226-
后置约束-排除 记作 `(?<!...)` 用于筛选所有匹配结果, 筛选条件为 其前不跟着定义的格式. 例如, 表达式 `(?<!(T|t)he\s)(cat)`匹配 `cat`, 且其前不跟着 `The``the`.
249+
后置约束-排除 记作 (?<!exp)用于筛选所有匹配结果, 筛选条件为 其前不跟着定义的格式. 例如, 表达式 `(?<!(T|t)he\s)(cat)`匹配 `cat`, 且其前不跟着 `The``the`.
227250

228251
```
229252
"(?<![T|t]he\s)(cat)" => The cat sat on cat.
@@ -275,6 +298,10 @@ Back引用(\n)是说在后面的表达式中我们可以使用组的编号
275298
on the mat.
276299
```
277300

278-
# [3. learn-regex](https://github.com/zeeshanu/learn-regex)
301+
# 3. 更多正则表达式教程
302+
303+
[learn-regex](https://github.com/zeeshanu/learn-regex):一个学习正则的开源项目,由浅入深,很容易入手,而且教程有中文版。
304+
305+
[正则表达式30分钟入门教程](https://deerchao.net/tutorials/regex/regex.htm#mission)
279306

280-
一个学习正则的开源项目,由浅入深,很容易入手,而且教程有中文版
307+
[Java正则表达式的应用](http://www.cnblogs.com/kissazi2/p/3287206.html)

第4章 常见对象/正则表达式的应用.md

Lines changed: 162 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -137,26 +137,6 @@ public class RegexDemo {
137137
}
138138
```
139139

140-
```java
141-
public static String setHtmlCotentSupportImagePreview(String body) {
142-
// 读取用户设置:是否加载文章图片--默认有wifi下始终加载图片
143-
if (AppContext.get(AppConfig.KEY_LOAD_IMAGE, true)
144-
|| TDevice.isWifiOpen()) {
145-
// 过滤掉 img标签的width,height属性
146-
body = body.replaceAll("(<img[^>]*?)\\s+width\\s*=\\s*\\S+", "$1");
147-
body = body.replaceAll("(<img[^>]*?)\\s+height\\s*=\\s*\\S+", "$1");
148-
// 添加点击图片放大支持
149-
// 添加点击图片放大支持
150-
body = body.replaceAll("(<img[^>]+src=\")(\\S+)\"",
151-
"$1$2\" onClick=\"showImagePreview('$2')\"");
152-
} else {
153-
// 过滤掉 img标签
154-
body = body.replaceAll("<\\s*img\\s+([^>]*)\\s*>", "");
155-
}
156-
return body;
157-
}
158-
```
159-
160140
### 1.4 获取功能
161141

162142
Pattern和Matcher类的使用
@@ -190,7 +170,7 @@ public class RegexDemo {
190170
}
191171
}
192172
```
193-
- Pattern 匹配模式
173+
### 1.5 Pattern 匹配模式
194174

195175
Pattern类为正则表达式的编译表示形式。指定为字符串的表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一个模式。
196176

@@ -199,18 +179,115 @@ Pattern类为正则表达式的编译表示形式。指定为字符串的表达
199179
| compile() | 把正则表达式编译成匹配模式 |
200180
| matcher() | 根据匹配模式去匹配指定的字符串,得到匹配器 |
201181

202-
- Matcher 匹配器
182+
### 1.6 Matcher 匹配器
203183

204-
| 方法声明 | 功能描述 |
205-
| :-------- | :----------- |
206-
| matches() | 匹配字符串 |
207-
| find() | 查找有没有满足条件的子串 |
208-
| group() | 获取满足条件的子串 |
184+
| 方法声明 | 功能描述 |
185+
| :------------------------ | :--------------------------------------- |
186+
| matches() | 匹配字符串 |
187+
| find() | 查找有没有满足条件的子串 |
188+
| group() | 获取满足条件的子串 |
189+
| reset() | 将Matcher的状态重新设置为最初的状态 |
190+
| reset(CharSequence input) | 重新设置Matcher的状态,并且将候选字符序列设置为input后进行Matcher, 这个方法和重新创建一个Matcher一样,只是这样可以重用以前的对象。 |
191+
| start() | 返回Matcher所匹配的字符串在整个字符串的的开始下标 |
192+
| start(int group) | 指定你感兴趣的sub group,然后返回sup group(子分组)匹配的开始位置。 |
193+
| end() | 返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
209194

210195
- 注意事项
211196

212197
Pattern类为正则表达式的编译表示形式。指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建Matcher对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式
213198

199+
分组:简单的说,分组其实就是为了能够指定同一个规则可以使用多少次。正则表达式中的分组就是整个大的正则表达式和用()圈起来的内容。
200+
201+
在这个正则表达式"\\w(\\d\\d)(\\w+)"中
202+
203+
- 分组0:是"\\w(\\d\\d)(\\w+)"
204+
- 分组1:是(\\d\\d)
205+
- 分组2:是(\\w+)
206+
207+
如果我们稍稍变换一下,将原先的正则表达式改为"(\\w)(\\d\\d)(\\w+)",我们的分组就变成了
208+
209+
- 分组0:是"\\w(\\d\\d)(\\w+)"
210+
- 分组1:是"(\\w)"
211+
- 分组2:是"(\\d\\d)"
212+
- 分组3:是"(\\w+)"
213+
214+
我们看看和正则表达式”\\w(\\d\\d)(\\w+)”匹配的一个字符串A22happy
215+
216+
- group(0)是匹配整个表达式的字符串的那部分A22happy
217+
- group(1)是第1组(\d\d)匹配的部分:22
218+
- group(2)是第2组(\w+)匹配的那部分happy
219+
220+
```java
221+
public static void main(String[] args) {
222+
String Regex="\\w(\\d\\d)(\\w+)";
223+
String TestStr="A22happy";
224+
Pattern p=Pattern.compile(Regex);
225+
Matcher matcher=p.matcher(TestStr);
226+
if (matcher.find()) {
227+
int gc=matcher.groupCount();
228+
for (int i = 0; i <= gc; i++) {
229+
System.out.println("group "+i+" :"+matcher.group(i));
230+
}
231+
}
232+
}
233+
```
234+
235+
- start()方法的使用
236+
237+
```java
238+
public static void testStart(){
239+
//创建一个 Matcher ,使用 Matcher.start()方法
240+
String candidateString = "My name is Bond. James Bond.";
241+
String matchHelper[] ={" ^"," ^"};
242+
Pattern p = Pattern.compile("Bond");
243+
Matcher matcher = p.matcher(candidateString);
244+
//找到第一个 'Bond'的开始下标
245+
matcher.find();
246+
int startIndex = matcher.start();
247+
System.out.println(candidateString);
248+
System.out.println(matchHelper[0] + startIndex);
249+
//找到第二个'Bond'的开始下标
250+
matcher.find();
251+
int nextIndex = matcher.start();
252+
System.out.println(candidateString);
253+
System.out.println(matchHelper[1] + nextIndex);
254+
}
255+
```
256+
257+
运行结果:
258+
259+
![](img/regex.png)
260+
261+
```java
262+
/**
263+
* 测试matcher.group方法
264+
*/
265+
public static void testGroup() {
266+
// 创建一个 Pattern
267+
Pattern p = Pattern.compile("Bond");
268+
// 创建一个 Matcher ,以便使用 Matcher.group() 方法
269+
String candidateString = "My name is Bond. James Bond.";
270+
Matcher matcher = p.matcher(candidateString);
271+
// 提取 group
272+
matcher.find();
273+
System.out.println(String.format("group匹配的字符串 : %s",matcher.group()));
274+
System.out.println(String.format("匹配的开始位置 : %d", matcher.start()));
275+
System.out.println(String.format("匹配的结束位置 : %d", matcher.end()));
276+
277+
System.out
278+
.println("---再次使用matcher.find()方法,看看matcher中group、start、end方法的效果");
279+
matcher.find();
280+
System.out.println(String.format("group匹配的字符串 : %s",matcher.group()));;
281+
System.out.println(String.format("匹配的开始位置 : %d", matcher.start()));
282+
System.out.println(String.format("匹配的结束位置 : %d", matcher.end()));
283+
System.out.println(String.format("candidateString字符串的长度 : %d", candidateString.length()));
284+
}
285+
```
286+
287+
运行结果:
288+
289+
![](img/regex1.png)
290+
214291
- 获取由三个字符组成的单词
215292

216293
```java
@@ -261,6 +338,64 @@ public class RegexDemo2 {
261338
}
262339
```
263340

341+
- 判断身份证:要么是15位,要么是18位,最后一位可以为字母,并写程序提出其中的年月日。
342+
343+
```java
344+
public static void main(String[] args) {
345+
testID_Card();
346+
}
347+
348+
public static void testID_Card() {
349+
// 测试是否为合法的身份证号码
350+
String[] strs = { "130681198712092019", "13068119871209201x",
351+
"13068119871209201", "123456789012345", "12345678901234x",
352+
"1234567890123" };
353+
// 准备正则表达式(身份证有15位和18位两种,身份证的最后一位可能是字母)
354+
String regex = "(\\d{14}\\w)|\\d{17}\\w";
355+
// 准备开始匹配,判断所有的输入是否是正确的
356+
Pattern regular = Pattern.compile(regex); // 创建匹配的规则Patter
357+
358+
StringBuilder sb = new StringBuilder();
359+
// 遍历所有要匹配的字符串
360+
for (int i = 0; i < strs.length; i++) {
361+
362+
Matcher matcher = regular.matcher(strs[i]);// 创建一个Matcher
363+
sb.append("身份证: ");
364+
sb.append(strs[i]);
365+
sb.append(" 匹配:");
366+
sb.append(matcher.matches());
367+
System.out.println(sb.toString());
368+
sb.delete(0, sb.length());// 清空StringBuilder的方法
369+
}
370+
371+
GetBirthDay(strs);
372+
373+
}
374+
375+
private static void GetBirthDay(String[] strs) {
376+
System.out.println("准备开始获取出生日期");
377+
// 准备验证规则
378+
Pattern BirthDayRegular = Pattern.compile("(\\d{6})(\\d{8})(.*)");
379+
// .*连在一起就意味着任意数量的不包含换行的字符
380+
Pattern YearMonthDayRegular = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})");
381+
for (int i = 0; i < strs.length; i++) {
382+
Matcher matcher = BirthDayRegular.matcher(strs[i]);
383+
384+
if (matcher.matches()) {
385+
Matcher matcher2 = YearMonthDayRegular.matcher(matcher.group(2));
386+
if (matcher2.matches()) {
387+
System.out.println(strs[i]+" 中的出生年月分解为: "+"" + matcher2.group(1) + " 月:" + matcher2.group(2) + " 日:" + matcher2.group(3));
388+
389+
}
390+
}
391+
}
392+
}
393+
```
394+
395+
运行结果:
396+
397+
![](img/regex2.png)
398+
264399
## 2. 正则表达式工具类
265400

266401
```java

0 commit comments

Comments
 (0)