简介
- 概念
正则表达式(Regular Expression)是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。 - 功能
(1)测试字符串内的模式。
例如,可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证。
(2)替换文本。
可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
(3)基于模式匹配从字符串中提取子字符串。
可以查找文档内或输入域内特定的文本。 - 特点
正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
语法
普通字符
字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是“普通字符”。表达式中的普通字符,在匹配一个字符串的时候,匹配与普通字符相同的一个字符。
即如果想要匹配一个字符串中的普通字符,直接输入对应普通字符就好了。
简单的转义字符
符号 | 含义 |
---|---|
\n | 代表换行符 |
\t | 制表符 |
\\ | 代表\本身 |
\^, \$, \., \ (, \ ), \{, \}, \?, \+, \*, \|, \ [, \] | 匹配这些字符本身 |
标准字符集合
- 能够与 ‘多种字符’ 匹配的表达式
- 注意区分大小写,大写是相反的意思
符号 | 含义 |
---|---|
\d | 任意一个数字,0~9中的任意一个 |
\w | 任意一个字母或数字或下划线,也就是A~Z,a~z,0~9,_ 中任意一个 |
\s | 包括空格、制表符、换行符等空白字符的其中任意一个 |
. | 小数点可以匹配除 换行符 外的任意一个字符,如果要匹配包括“\n”在内的所有字符,一般用“[\s\S]” |
自定义字符集合
- [ ] 方括号匹配方式,能够匹配方括号中 任意一个 字符。
表达式 | 含义 |
---|---|
[ab5@] | 匹配“a”或“b”或“5”或“@” |
[^abc] | 匹配”a“,”b“,”c“之外的任意一个字符 |
[f-k] | 匹配”f“~”k“之间的任意一个字母 |
[^A-F0-3] | 匹配”A“~”F“,”0“~”3“之外的任意一个字符 |
注意
- 正则表达式的特殊符号,被包含到中括号中,则失去特殊意义,除了^, -之外。比如
[\s\S]
,即是包含\s
或\S
集合中的任一字符,即是所有字符。 - 标准字符集合,除小数点外,如果被包含于中括号,自定义字符集合将包含该集合。比如:
[\d.\-+]
将匹配:数字、小数点、-、+
量词
- 修饰匹配次数的特殊符号
符号 | 含义 |
---|---|
{n} | 表达式重复n次 |
{m,n} | 表达式至少重复m次,最多重复n次 |
{m,} | 表达式至少重复m次 |
? | 匹配表达式0次或1次,相当于{0,1} |
+ | 表达式至少出现1次,相当于{1,} |
* | 表达式不出现或出现任意次,相当于{0,} |
注意
- 默认匹配的字符数是最多的,即尽可能的多。(因为次数有时是一个范围嘛)
- 如果想匹配的字符是最少的,可以在符号后面添加“?”号,此时匹配的字符数尽可能的少。比如
{m,n}?
。
字符边界
- 本组标记匹配的不是字符而是位置,即符和对应条件的位置。
标记 | 含义 |
---|---|
^ | 与字符串开始的地方匹配 |
$ | 与字符串结束的地方匹配 |
\b | 匹配一个单词边界 |
注意
\b
匹配这样一个位置:前面的字符和后面的字符 不全是 \w。\b222\b
:其中两个“\b”分别表示的是数字 222 前后的位置,比如在字符串ac222de
中,分别表示的是c和2之间 和 2和d之间的位置。
选择符和分组
- 分组
表达式 | 作用 |
---|---|
| - 分支结构 | 左右两边表达式之间“或”关系, 匹配左边或者右边 |
() - 捕获组 | (1)在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰;(2)取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到;(3)每一对括号会分配一个编号,使用()的捕获根据左括号的顺序从1开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达式模式匹配的文本 |
(?:Expression) - 非捕获组 | 一些表达式中,不得不使用(),但又不需要保存()中子表达式匹配的内容,这时可以使用非捕获组来抵消使用()带来的副作用 |
- 反向引用(\nnn)
(1)每一对()会分配一个编号,使用 () 的捕获根据左括号的顺序从1开始自动编号。
(2)通过反向引用,可以对分组已捕获的字符串进行引用。
预搜索(零宽断言)
- 只进行子表达式的匹配,匹配内容不计入最终的匹配结果,是零宽度。
- 判断当前位置的前后字符,是否符和指定的条件,但不匹配前后的字符。是对位置的匹配。
- 正则表达式匹配过程中,如果子表达式匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。占有字符还是零宽度,是针对匹配的内容是否保存到最终的匹配结果中而言的。
- 匹配满足断言条件的字符串。不包含断言中的exp内容。
表达式:
表达式 | 含义 |
---|---|
(?=exp) | 断言自身出现的位置的后面能匹配表达式exp |
(?<=exp) | 断言自身出现的位置的前面能匹配表达式exp |
(?!exp) | 断言此位置的后面不能匹配表达式exp |
(?<!exp) | 断言此位置的前面不能匹配表达式exp |
使用流程
- 分析所要匹配的数据,写出测试用的典型数据。
- 在工具软件中进行匹配测试。比如使用RegexBuddy
- 在程序中调用通过测试的正则表达式。
java编程中的应用
- 基本使用方法
代码:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 测试正则表达式在java编程中的基本使用方法
* @author dxt
*
*/
public class Demo01 {
public static void main(String[] args){
//1 判断字符串 “aabbcc22666” 是否符合指定的正则表达式:\w+
//1.1 表达式对象(正则表达式 ---> java中的对象)
Pattern p = Pattern.compile("\\w+");
//1.2创建Matcher对象(将正则表达式与目标字符串关联起来)
Matcher m = p.matcher("aabbcc22666");
//1.3 进行匹配判断
boolean result = m.matches(); //尝试将整个字符序列与该模式匹配
System.out.println(result);
//2. 判断字符串序列“aabbcc2&&2666”, 是否符和指定的正则表达式:\w+
Matcher m2 = p.matcher("aabbcc2&&2666");
System.out.print(m2.find() + "\t"); //true - aabbcc2 find()扫描输入的序列,查找与该模式匹配的下一个子序列
System.out.println(m2.group()); //输出上一次进行匹配的字符串
System.out.print(m2.find() + "\t"); //true - 2666
System.out.println(m2.group());
System.out.println(m2.find()); //false
}
}
结果
2. group()方法
代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 测试group()方法的使用
* @author dxt
*
*/
public class Demo02 {
public static void main(String[] args){
//1. 表达式对象
Pattern p = Pattern.compile("([a-z]+)([0-9]+)");
//2 创建Matcher对象
Matcher m = p.matcher("aa232*ssd445*sbs223");
while(m.find()){
System.out.println(m.group()); //group() 与 group(0)匹配整个表达式的子字符串
System.out.println(m.group(1));
System.out.println(m.group(2));
}
}
}
结果
3. 替换与分割
代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 1. 测试正则表达式的替换操作
* 2. 测试正则表达式的分割操作
* @author dxt
*
*/
public class Demo03 {
public static void main(String[] args){
//1.1 表达式对象
Pattern p = Pattern.compile("[0-9]");
//1.2 创建Matcher 对象
Matcher m = p.matcher("aa232*ssd445*sbs223");
//替换
String s1 = m.replaceAll("#"); //将数字替换为 #
System.out.println(s1);
//2 分割
String str1 = "aa232**ssd445**sbs223";
String[] arr = str1.split("\\*+"); //可以将多个*作为一个边界
for(String temp : arr){
System.out.println(temp);
}
String str2 = "aa232ssd4456sbs223";
String[] arr2 = str2.split("\\d+");
for(String temp : arr2){
System.out.println(temp);
}
}
}
结果
4. 获取字符串中第i个数
代码:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 测试使用正则表达式 提取字符串中的 数字
* @author dxt
*
*/
public class TestRegex {
/**
* 返回字符串str中出现的第index个数
* @param str
* @param index
* @return
*/
public static int getNum(String str, int index) {
//1 数据校验
if(index < 1) {
throw new RuntimeException("index输入错误。");
}
//1.1 正则表达式对象
Pattern p = Pattern.compile("[0-9]+");
//1.2 创建Matcher对象
Matcher m = p.matcher(str);
//2 获取结果
int i = 1;
while(m.find()) {
if(i == index) {
return Integer.parseInt(m.group()); //返回对应数字
}
i++;
}
return -1; //当 index 大于自符串中的个数时
}
public static void main(String[] args) {
String str = "7+2-30*5/4";
String str1 = "-0+2/3-444*55";
String str2 = "10 + 20 -30 /40 * 50 = 000";
System.out.println(getNum(str2, 5));
}
}
使用正则表达式获取网页标签内容
- 根据URL获取网页源码
- 从源码中匹配标签内容
代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 使用正则表达式爬取网页中的链接内容
* 链接标签<a href=""></a>
* @author dxt
*
*/
public class WebSpider {
/**
* 获取url对应的源代码
* @param url
* @param charset
* @return
*/
public static String getUrlContent(String url, String charset){
StringBuilder sb = new StringBuilder();
try {
URL u = new URL(url);
BufferedReader br = new BufferedReader(new InputStreamReader(u.openStream(), charset));
String content = null;
while((content=br.readLine()) != null){
sb.append(content);
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
public static void main(String[] args){
//获取网页源代码
String destStr = getUrlContent("http://www.163.com", "gbk");
//在源代码中匹配对应的内容
// Pattern p = Pattern.compile("<a.+?</a>"); //获取整个a标签的内容
Pattern p2 = Pattern.compile("href=\"(.+?)\""); //获取地址
Pattern p3 = Pattern.compile("(?<=href=\")(.+?)(?=\")"); //零宽断言获取地址
Matcher m = p3.matcher(destStr);
while(m.find()){
System.out.println(m.group());
System.out.println(m.group(1));
}
}
}