使用正则表达式对信息进行提取

正则表达式通过按一定的语法规则在一段文字中进行匹配,然后返回匹配结果。

匹配类型:

在一段文本中发现石否包含某个子串
抽取出符合正则表达式语法的子串
对符合正则表达式的子串进行替换
返回一个布尔值,判断是否匹配

在 Java 中使用正则表达式的方法如下:
Java 中关于正则表达式有两个类,一个接口和一个异常

1
2
3
4
Java.util.regex.Pattern;
Java.util.regex.Matcher;
Java.util.regex.MatchResult;
Java.util.regex.PatternSyntaxException;

一个 Pattern 对象是一个编译的正则表达式,可以用于任何字符串。 一个 Matcher 对象
是一个单个的用于一个特别目标字符串的实例。 MatchResult 封装了一次成功匹配的数
据。
Pattern.compile()方法将一个字符串编译成正则表达式。 即得到一个 Pattern 对象。

1
2
3
4
5
6
7
8
9
String regex="(<String?>.+?</String?>)";
String str="<id>4654165ashdjfkl</id><String>QWEHASOIUASC</String>";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
String s;
if(m.find()){
s = m.group(1);
System.out.println(s);
}

上例中,字符串 regex 是我们写好的正则表达式的字符串。 变量 str 是待匹配的字符串。
Pattern p = Pattern.compile(regex);
将正则表达式字符串编译,得到一个 Pattern 对象。

Pattern 对象下的 matcher()方法是使用当前编译的正则表达式去匹配字符串,然后得到一个 Matcher 对象。 Matcher 对象的 find()方法判断是否匹配成功。 正则表达式的一次匹配结果可以包含多个子串,用 Matcher 对象的 group 方法来获得匹配结果的多个子串。 其参数是一次匹配中第几个匹配的子串。如上图是一次匹配中的第一个匹配的子串。 (注:一个正则表达式可以从匹配的字符串中抽取多个子串,其中子串用括号来表示,如上例中有一对括号,因此就有一个匹配的子串。 )

正则表达式

一些 meta characters:
(1)^ 开始字符。例如,^cat 表示匹配一段文字。 这一段文字如果以‘cat’开始,则可以匹配

(2)$ 结束字符。例如,cat$,表示匹配以 cat 结尾的一段文字

(3)[ ] 字符类,列举想要匹配的字符。例如,gr[ea]y,表示匹配含有 grey 或gray 子串的文字

(4)- 在字符类内部使用- 表示范围。例如,[0123456789abcdEFGH]可以写成[0-9a-dE-H]。 注:“-”仅仅在字符类内部是 meta character。

(5)在字符类内部使用^表示非。[^1-6]表示匹配一个字符不是 1 到 6 的。

(6). 匹配任意字符。但在字符类[.]内的“.”就是它的字面意思而不是特殊字符。例如正则表达式 03[.]16[.]76 可以匹配 03.16.76,03\16\76,03.16\76,03\16.76。再看正则表达式 “ [0-9].[0-9]” ,表达的是匹配包含下来子串的字符串“ 数字任意字符数字” 。 即子串的第一个字符是数字,第二个字符是任意字符,第三个字符是数字

(7)| 表示或。例如,Bob|Robert 表示匹配 Bob 或 Robert。正则表达式 Gr[ea]y 可以写成 grey|gray。 也可以写成 gr(e|a)y。但 gr[e|a]y 中的“|”不是特殊字符,是其字面意思。 gr(e|a)y 中括号约束了替换的范围,即两个字符 e 和 a 的替换。

(8)? 表示选择,即在?前面的字符是可选择的。colou?r 即匹配 color 或者 colour。4(th)?表示匹配 4 或者 4th。此处括号对可选择的多个字符做了限制

(9)+ 表示一个或多个立即跟随的字符。例如,[0-9]+表示任意长的数字,012,,91 等

(10)\ 表示后面跟的字符是字面含义,而不是特殊字符。例如,ega.att.com。此处的“.”不是特殊字符而是 literal。 匹配 ega.att.com。

(11){min,max} 定义匹配的范围。例如, [a-z]{1,3}表示字母 a-z 可以出现一次,最多三次。

(12)( ) 有两种含义,一是表示限定范围;二是,当从字符串中取出子串时,括号规定了要提取的子串。

(13)> 特殊字符:

\d 表示数字
\D 表示非数字,等于[^\d]
\w 等同于[a-zA-Z0-9]
\W 等同于 [^\w]
\s 空白符等同于[\f\n\r\t\v]
\S 非空白符
\b 匹配一个 backspace 或 tab 字符

例:

(1)

1
2
3
4
5
String regex="q[^u]";
String str="Iraq";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
System.out.println(m.find());

结果是false。该正则表达式,要匹配的字符串包含字符q,并且后面必须跟着一个不是u的字符。

(2)

1
2
3
4
5
String regex="q[^u]";
String str="Qantas";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
System.out.println(m.find());

结果还是false。应为正则表达式对大小写敏感。如果想要消除这种敏感,编译正则表达式时使用

1
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

(3)
两个正则表达式
^From|Subject|Date:
^(From|Subject|Date):
匹配结果有什么不同

1
2
3
4
5
6
7
8
9
String regex="^From|Subject|Date:";
String regex1="^(From|Subject|Date):";
String str="hello Subject: dallas";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
attern p1 = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
Matcher m1 = p1.matcher(str);
System.out.println(m.find());
System.out.println(m1.find());

结果是

1
2
true
false

这是因为,第一个正则表达式^From|Subject|Date: 表示匹配^From(被匹配的字符串必须以 From 开头)或者Subject (包含子串 Subject) 或者Date:(包含字符串Date:)。第二个正则表达式^(From|Subject|Date):表示匹配的字符串必须以From:或者Subject: 或者Date:开头。

(4)

1
2
3
4
5
String regex="^h.+d$";
String str="hello world";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
System.out.println(m.find());

结果是true。该正则表达式匹配以h开头,d结尾的字符串。.+表示匹配任意多个字符

(5)

1
2
3
4
5
String regex="hello\\.world";
String str="Hi, hello.world, Yes";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
System.out.println(m.find())

这段代码中,要匹配一个子串“hello.world”。 因为正则表达式中“.”是特殊字符。因此要匹配该子串,我们写的正则表达式应该是“hello.world”,这里的“\”表示后面的“.”是其字面意思,不是特殊字符。而在 java 中“\”又是特殊字符,我们就再加上一个“\”字符。因此得到了最终的正则表达式”hello\.world”。

(6)我们要提取字符串“ price is 34$, today is April 12” 中描述的价格。

1
2
3
4
5
6
7
8
9
String regex="([0-9]+)\\$";
String str="price is 34$, today is April 11";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
String s;
if(m.find()){
s=m.group(1);
System.out.println(s);
}

美元符号$在正则表达式中是特殊字符,当前的例子中,文本中包含了美元符号,因此在写正则表达式时,需要在它的前面加上“\”表示其后的美元符号不是特殊字符。另外,括号的作用在于指示这是要提取出的子串。 因此程序运行结果是 34。

(7)匹配字符串“ price is 34$, today is April 12” 中的所有数字,即 34 和 11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String regex="([0-9]+)";
String str="price is 34$, today is April 11";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m;
String s;
Integer location=0;
while(location<str.length()){
m = p.matcher(str);
if(m.find(location)){
s=m.group(1);
System.out.println(s);
location=m.end();
}else{
break;
}
}

匹配的结果中包含了匹配的子串的起始位置和结束位置,可以用 Matcher 对象的 start()方法和 end()方法来获得。 定义一个 Integer 类型的变量 location,它记录每趟循环中匹配到的子串的结束位置。find(location)方法,表示从当前 location 的位置去寻找匹配的子串。因此在 while 循环中多次去匹配字符串,发现字符串多个与正则表达式匹配的子串。

(8)对于包含小数点的数字的提取

1
2
3
4
5
6
7
8
9
10
String regex="([0-9]+\\.[0-9]+)";
String str="price is 343.4$, today is April 11";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m;
String s;
m = p.matcher(str);
if(m.find()){
s=m.group(1);
System.out.println(s);
}

字符串中混合了小数和整数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String regex="([0-9]+(\\.[0-9]+)?)";
String str="price is 343.4$, today is April 11";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m;
String s;
Integer location=0;
while(location<str.length()){
m = p.matcher(str);
if(m.find(location)){
s=m.group(1);
System.out.println(s);
location=m.end();
}else{
break;
}
}

该正则表达式中有两对括号,其中一对嵌入在另一对中。 里面的那一对后面有个?,是用来指示括号里面的内容是“可选择的”。 匹配的子串是外面的那一对括号。

(9)提取出时间

1
2
3
4
5
6
7
8
9
10
String regex="([0-9]?[0-9]:[0-9][0-9]\\s(am|pm)?)";
String str="It is 9:21 am";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher m;
String s;
m = p.matcher(str);
if(m.find()){
s=m.group(1);
System.out.println(s);
}

贪婪模式和非贪婪模式

贪婪模式,正则表达式默认的是最大匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String regex="<[a-zA-Z]+>.+</[a-zA-Z]+>";
String str="<p>hello</p><div>world</div>";
Pattern p = Pattern.compile(regex);
Matcher m;
String s;
Integer location=0;
while(location<str.length()){
m = p.matcher(str);
if(m.find(location)){
s=m.group(0);
System.out.println(s);
location=m.end();
}else{
break;
}
}

结果是

1
<p>hello</p><div>world</div>

使用非贪婪模式,正则表达式中的“*”或“+”后加上一个 ?符号,称为惰性符号<[a-zA-Z]+>.+?</[a-zA-Z]+>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String regex="<[a-zA-Z]+>.+?</[a-zA-Z]+>";
String str="<p>hello</p><div>world</div>";
Pattern p = Pattern.compile(regex);
Matcher m;
String s;
Integer location=0;
while(location<str.length()){
m = p.matcher(str);
if(m.find(location)){
s=m.group(0);
System.out.println(s);
location=m.end();
}else{
break;
}
}

结果是

1
2
<p>hello</p>
<div>world</div>