Java 正则表达式
文章目录
意义
- 正则表达式是对字符串操作的一种逻辑公式
- 事先定义好的特定字符、及特定字符的组合组成规则字符串
- 表达对字符串的一种过滤逻辑
给定一个正则表达式和另一个字符串可以实现:
-
字符串是否符合正则表达式的过滤逻辑(“匹配”)
-
通过正则表达式从字符串中获取特定部分
特点
-
灵活性、逻辑性和功能性非常强
-
可以迅速地用极简单的方式达到字符串的复杂控制
-
对于刚接触的人来说,比较晦涩难懂
-
正则表达式写好后,没有错对之分,返回结果只是true和false
-
定义了字符串的模式,可以用来搜索、编辑或处理文本
- 并不局限某种语言,但在不同语言有细微差别
实例:一个字符串就是一个简单的正则表达式
- Hello World 正则表达式匹配"Hello World"字符串
.
(点号)也是一个正则表达式,匹配任何一个字符:“a” 或 “1”
正则表达式 | 描述 |
---|---|
this is text | 匹配字符串"this is text" |
this\s+is\s+text | 注意\s+ 可以匹配多个空格,之后匹配is字符串,再之后\s+匹配多个空格再跟上text字符串;例:this is text |
^\d+(\.\d+)? | ^ 定义了以什么开始 \d+ 匹配一个或多个数字 ? 设置括号内的选项是可选的 \. 匹配 “.” 可以匹配的实例:“5”, “1.5” 和 “2.21” |
String
string
类型字符串可以转为其他任何类型,所以用户输入内容一般用 String
接收
- 转为其他类型数据为确保正确性需要复杂验证
- 正则表达式就可以对字符串内容进行拆分、验证
- 通常用来进行通用内容格式验证,或内容的定向读取
//String 中可以直接使用正则表达式
//matcher(): 判断是否相同,返回布尔值
String s = "456137900";
System.out.println(s.matcher("\\d+")); //结果为true,匹配所有数字
//split(): 分割字符串
String content = "hello,world!--你好啊?世界";
String[] split = content.split(",|!--|?");
int i = 0;
for (String s1 : split){
System.out.println(++i + ": " + s1);
}
运行结果:
1: hello
2: world
3: 你好啊
4: 世界
//哑巴去重可写为下列代码
String a = "我我...要要要....学学..java";
a = a.replaceAll("\\.*", ""); //将所有的。 换成""
a = a.replaceAll("(.)\\1*", "$1"); //将所有重复内容换为第一个
regex包
Pattern类
Pattern
对象是正则表达式的编译表示- 没有公共构造方法,调用其公共静态方法返回一个
Pattern
对象- 该方法接受一个正则表达式作为它第一个参数
- 提供正则表达式编译处理
public static Patterm compile(String regex)
- 字符串拆分操作
public String[ split(CharSequence input)
- 没有公共构造方法,调用其公共静态方法返回一个
Matcher类
用法
-
实现正则匹配的处理类
-
靠
Pattern
类实例化public Matcher matcher(CharSequence input)
-
获取Matcher对象之后就可以进行该类方法进行正则操作
-
正则匹配:
public boolean matchers()
-
String connect = "I am noob" + "from runoob.com"; // 字符串内容 String pattern = ".*runoob.*"; // 定义正则表达时 System.out.println(Pattern.matches(pattern, connect)); // 匹配内容中是否有符合要求的内容
-
-
字符串替换:
public String replaceAll(String replacement)
-
-
以拆分,替换,匹配三种操作用不到 java.util.regex 开发包,只依靠 String 类可以实现
- 但 mather 类里面提供有一种分组的功能,而这种分组的功能是 String 不具备的
捕获组
用法
捕获组是把多个字符当作一个单独单元进行处理的方法,通过对括号内字符分组来创建
- 例如:正则表达式(dog)创建了单一分组,包含"d",“o”,“g”
捕获组通过自左至右计算其开括号来编号,依次排序(从1开始)
-
例如:表达式
((A)(B(C)))
-
有四个组
((A)(B(C)))
、(A)
、(B(C))
、(C)
- 用括号组成较复杂的匹配模式,一个括号部分看作一组(一个子表达式)
-
正则表达式中分组(子表达式)匹配到的内容,保存到内存中以数字编号命名或显示命名的组里,方便后面引用
-
从左向右,以分组的左括号为标志,依次排序(从1开始)
- 组 0 代表整个表达式
-
显示命名
String reg = "(?i)(?<g>Bi)"; // (?i)(要筛选的字符): 忽略大小写 // (?<name>正则式): 给分组命名
-
-
调用
matcher
对象的groupCount
方法来查看表达式分组数量- 返回 int 值表示 matcher 对象当前有多个捕获组
-
特殊的组(group(0))代表整个表达式,不在返回值内
-
-
public class RegexMatches{ public static void main( String[] args ){ // 按指定模式在字符串查找 String line = "This order was placed for QT3000! OK?"; String pattern = "(\\D*)(\\d+)(.*)"; Pattern r = Pattern.compile(pattern); // 创建 Pattern 对象 Matcher m = r.matcher(line); // 创建 matcher 对象 if (m.find( )) { // 找到匹配内容 System.out.println("全部 : " + m.group(0) ); // 全部内容 System.out.println("\\D* : " + m.group(1) ); // 第一组 \\D* 非数字 0个 ~ 无穷 System.out.println("\\d+ : " + m.group(2) ); // 第二组 \\d+ 数字 1个 ~ 无穷 System.out.println(".* : " + m.group(3) ); // 第三组 .* 任意内容到最后 } else { System.out.println("NO MATCH"); } } } /** 运行结果: 全部 :This order was placed for QT3000! OK? \\D* : This order was placed for QT \\d+ : 3000 .* : ! OK? */
URL
(?<name>正则式)
: 给分组命名- 可以通过分组名获取分组内容
//要查找的内容
String s = "https://www.bilibili.com/video/BV1fh411y7R8?p=894&spm_id_from=pageDriver";
//正则表达式
String url = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?$&%#=_/.]*)?$";
/*
^((http|https)://)? 匹配 https://
([\\w-]+\\.)+ 匹配 www.bilibili.
[\\w-]+ 匹配 com
(\\/[\\w-?$&%#=_/.]*)?$ 匹配 /video/BV1fh411y7R8?p=894&spm_id_from=pageDriver
*/
/*
String a = "BIBILIlibili";
String reg = "(?i)(?<g>Bi)";
// (?i)(要筛选的字符): 忽略大小写
// (?<name>正则式): 给分组命名
while(matcher.find()) //必须有判断是否有捕获组,否则报异常
System.out.println(matcher.group("g")); //用组名获取捕获组
*/
//创建模板对象,将正则表达式传入
Pattern compile = Pattern.compile(url);
//创建匹配器
Matcher matcher = compile.matcher(s);
//输出有分组数量
System.out.println(matcher.groupCount());
//判断有没有捕获组(按括号分组),必须判断,否则报异常
while (matcher.find()) {
//遍历输出所有捕获组
for (int i = 0; i <= matcher.groupCount(); i++) {
//matcher.group(0): 表示匹配到的所有内容
//mathcer.grou(i): i != 0 表示匹配到的group(0)中的子串(括号中匹配到的内容)
System.out.println(matcher.group(i));
}
}
/**
运行结果:
4 //捕获组数量(即括号数量)
https://www.bilibili.com/video/BV1fh411y7R8?p=894&spm_id_from=pageDriver //全部
https:// //第一组
https //第二组
bilibili. //第三组
/video/BV1fh411y7R8?p=894&spm_id_from=pageDriver //第四组
*/
反向引用
分组内容被捕获后可在这个括号之后使用,称为反向引用
-
可以是在正则表达式内部,内部反向引用:
\\分组号
String reg = "(abds)(?<g>Bi)\\1\\2"; //分为两组,第二组命名为 g // \\1: 反向引用第一组,即(abds)(?<g>Bi)(abds) // \\2: 反向引用第二组,不能使用命名引用 // 整个表达式等同于: (abds)(?<g>Bi)(abds)(Bi)
-
也可用在外部,外部反向引用:
$分组号
-
//结巴去重 String a = "我我...要要要....学学..java"; //找出所有的. String reg = "\\.*"; //捕获所有的. Matcher matcher = Pattern.compile(reg).matcher(a); //将所有的.替换为空 a = matcher.replaceAll(""); System.out.println(a); //表达式找到所有有重复的字段,使用内部反向引用得到有重复的内容 reg = "(.)\\1*"; //外部反向引用将捕获的所有重复内容替换为第一组的内容(仅一个字) a = Pattern.compile(reg).matcher(a).replaceAll("$1"); System.out.println(a); /** 运行结果: 我我要要要学学java 我要学java */
Matcher类方法
索引方法
方法 | 说明 |
---|---|
public int start() | 返回以前匹配的初始索引 |
public int start(int group) | 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引 |
public int end() | 返回最后匹配字符之后的偏移量。 |
public int end(int group) | 返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
- start 和 end
private static final String A = "\\bcat\\b"; // 匹配 c 开头 t 结尾的 cat
private static final String B="cat cat cat cattie";
public static void main(String[] args){
Pattern p = Pattern.compile(A);
Matcher m = p.matcher(B); //获取matcher对象
int count = 0;
while (m.find()){
count++;
System.out.println("Match number: " + count);
System.out.println("start():" + m.start());
System.out.println("end():" + m.end());
}
}
运行结果:
Match number: 1
start():0
end():3
Match number: 2
start():4
end():7
Match number: 3
start():8
end():11
// 使用单词边界,以确保字母 "c" "a" "t" 并非仅是一个较长的词的子串;
// Start 方法返回给定组所捕获的子序列的初始索引,end 方法返回最后一个匹配字符的索引加 1
查找方法
方法 | 说明 |
---|---|
public boolean lookingAt() | 尝试将从区域开头开始的输入序列与该模式匹配 |
public boolean find() | 尝试查找与该模式匹配的输入序列的下一个子序列 |
public boolean find(int start) | 重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列 |
public boolean matches() | 尝试将整个区域与模式匹配 |
-
matches 和lookingAt
-
matches 和 lookingAt 方法都用来尝试匹配一个输入序列模式,经常在输入字符串的开始使用
-
matches 要求整个序列都匹配
-
lookingAt 不要求,但 lookingAt 方法需要从第一个字符开始匹配
-
-
private static final String A = "foo";
private static final String B="fooooooooooooooooo";
private static final String C="ooooofoooooooooooo";
private static Pattern pattern;
private static Matcher matcher1;
private static Matcher matcher2;
public static void main( String[] args ){
pattern = Pattern.compile(A);
matcher1 = pattern.matcher(B);
matcher2 = pattern.matcher(C);
System.out.println("Current A is: "+ A);
System.out.println("Current B is: "+ B);
System.out.println("Current C is: "+ C);
System.out.println("lookingAt(): "+matcher1.lookingAt());
System.out.println("matches(): "+matcher1.matches());
System.out.println("lookingAt(): "+matcher2.lookingAt());
}
运行结果:
Current A is: foo
Current B is: fooooooooooooooooo
Current C is: ooooofoooooooooooo
lookingAt(): true
matches(): false
lookingAt(): false
替换方法
方法 | 说明 |
---|---|
public Matcher appendReplacement (StringBuffer sb, String replacement) | 实现非终端添加和替换步骤 |
public StringBuffer appendTail (StringBuffer sb) | 实现终端添加和替换步骤 |
public String replaceAll(String replacement) | 替换模式与给定替换字符串相匹配的输入序列的每个子序列 |
public String replaceFirst(String replacement) | 替换模式与给定替换字符串匹配的输入序列的第一个子序列 |
public static String quoteReplacement (String s) | 返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给 Matcher类 的appendReplacement 方法一个字面字符串一样工作 |
-
replaceFirst 和 replaceAll
-
replaceFirst 和 repalceAll 方法用来替换匹配正则表达式的文本
-
replaceFirst 替换首次匹配
-
replaceAll 替换所有匹配
-
-
private static String A = "dog"; private static String B = "The dog says meow. " + "All dogs say meow."; private static String REPLACE = "cat"; public static void main(String[] args) { Pattern p = Pattern.compile(A); Matcher m = p.matcher(B);//得到matcher对象 B = m.replaceAll(REPLACE); System.out.println(B); Pattern p1 = Pattern.compile(REPLACE); Matcher m1 = p1.matcher(B);//得到matcher对象 B = m1.replaceFirst(A); System.out.println(B); } 运行结果: The cat says meow. All cats say meow. The dog says meow. All cats say meow.
-
-
appendReplacement 和 appendTail
appendReplacement(StringBuffer sb, String replacement)
方法sb
是一个StringBuffer
replaceContext
是待替换的字符串- 把匹配到的内容替换为
replaceContext
- 并把从上次替换的位置到这次替换位置之间的字符串拿到
- 加上本次替换后的结果一起追加到
StringBuffer
-
appendTail
:把最后一次匹配到内容之后的字符串追加到StringBuffer
-
private static String A = "a*b"; //匹配 0 ~ 无穷个 a 以 一个b 结尾 private static String B = "aabfooaabfooabfoobkkk"; private static String REPLACE = "-"; public static void main(String[] args) { Pattern p = Pattern.compile(A); Matcher m = p.matcher(B);// 获取 matcher 对象 StringBuffer sb = new StringBuffer(); while(m.find()){ m.appendReplacement(sb, REPLACE); System.out.println(sb.toString()); } m.appendTail(sb);//添加最后匹配到的内容之后的字符 System.out.println(sb.toString()); // -foo-foo-foo-kkk } 运行结果: - -foo- -foo-foo- -foo-foo-foo- -foo-foo-foo-kkk
PatternSyntaxException
PatternSyntaxException
是一个非强制异常类,表示一个正则表达式模式中的语法错误
方法 | 说明 |
---|---|
public String getDescription() | 获取错误的描述 |
public int getIndex() | 获取错误的索引 |
public String getPattern() | 获取错误的正则表达式模式 |
public String getMessage() | 返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示 |
正则语法
-
其他语言中
\\
表示在正则表达式中插入一个普通的(字面上的)反斜杠 -
Java中
\\
表示:要插入一个正则表达式的反斜线,其后的字符具有特殊的意义- 其他的语言中一个反斜杠
\
就足以具有转义的作用 - 在 Java 中正则表达式需要两个反斜杠才被解析为转义作用
- 其他的语言中一个反斜杠
System.out.print("\\"); // 输出为 \
System.out.print("\\\\"); // 输出为 \\
- Java Language Specification 要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义
- 因此必须使用两个反斜线,表示正则表达式受到保护不被 Java 字节码编译器解释
- 例如,当解释为正则表达式时,字符串字面值
\b
与单个退格字符匹配,而 “\\b
” 与单词边界匹配 - 字符串字面值
\(hello\)
是非法的,将导致编译错误;要与字符串 (hello) 匹配,必须使用字符串字面值\\(hello\\)
- 例如,当解释为正则表达式时,字符串字面值
匹配格式
单个字符匹配
任意字符 //表示由任意字符组成
\\ //匹配 \
\n //匹配换行
\t //匹配制表符
单个字符集
- 可以从里面任选一个字符
[abc] //表示可能是字母 a、b、c中的任意一个;
[^abc] //表示不是由字母 a、b、C中的任意-一个;
[a-zA-Z] //表示由一个任意字母所组成,不区分大小写;
[0-9] //表示由一位数字所组成;
单个简化字符集
. //表示任意的一个字符;
\\d //等价于 [0-9] 范围;
\\D //等价于“[^0-9]”范围;
\\s //匹配任意的一位空格,可能是一个空格、换行、制表符;
\\S //匹配任意的非空格数据;
\\w //匹配字母、 数字、下划线, 等价于 [a-zA-Z0-9_]
\\W //匹配非字母、数字、下划线,等价于“[^a-zA-Z0-9_]”;
边界匹配
^ //匹配边界开始
$ //匹配边界结束
数量表示
默认情况下只有添加上了数量单位才可以匹配多位字符;
表达式? //该正则可以出现 0 次或 1 次;
表达式* //该正则可以出现 0 次、1 次或多次;
表达式+ //该正则可以出现 1 次或多次;
表达式{n} //表达式的长度正好为 n 次;
表达式{n, } //表达式的长度为 n 次或以上
表达式{n,m} //表达式的长度在 n~m 次;优先匹配多次,贪心匹配
逻辑表达式
- 可以连接多个正则
表达式 X 表达式 Y // X 表达式之后紧跟上 Y 表达式;
表达式 X | 表达式 Y //有一个表达式满足即可;
(表达式) //为表达式设置-个整体描述,可以为整体描述设置数量单位
正则格式列表
字符 | 说明 |
---|---|
\\ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符 例如: n匹配字符 n; \n 匹配换行符;序列 \\\\ 匹配 \\ ,\\(匹配 ( |
^ | 匹配输入字符串开始的位置,如果设置了 RegExp 对象的 Multiline 属性,^ 还会与\n 或\r 之后的位置匹配 |
$ | 匹配输入字符串结束的位置,如果设置了 RegExp 对象的 Multiline 属性,^ 还会与\n 或\r 之后的位置匹配 |
* | 零次或多次匹配前面的字符或子表达式,例如:zo* 匹配 z 和 zoo;* 等效于 {0,} |
+ | 一次或多次匹配前面的字符或子表达式, 如:zo+ 与 zo 和 zoo 匹配,但与 z 不匹配;+ 等效于 {1,} |
? | 零次或一次匹配前面的字符或子表达式,例如:do(es)? 匹配 do 或 does 中的 do;? 等效于 {0,1} |
{n} | n 是非负整数。正好匹配 n 次;例如:o{2} 与 Bob 中的 o 不匹配,但与 food 中的两个 o 匹配 |
{n,} | n 是非负整数。至少匹配 n 次;例如:o{2,} 不匹配 Bob 中的 o ,而匹配 foooood 中的所有 o。o{1,} 等效于o+ ;o{0,} 等效于o* |
{n,m} | m 和 n 是非负整数,其中 n <= m:匹配至少 n 次,至多 m 次 例如: o{1,3} 匹配 fooooood 中的头三个 o;o{0,1}等效于 o?`;注意:不能将空格插入逗号和数字之间 |
? | 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m} )之后时,匹配模式是非贪心的"非贪心的"模式匹配搜索到的、尽可能短的字符串; 默认的"贪心的"模式匹配搜索到的、尽可能长的字符串;例如:字符串 oooo 中, o+? 只匹配单个 o ,而o+ 匹配所有 o |
. | 匹配除\r\n 之外的任何单个字符若要匹配包括 \r\n 在内的任意字符,请使用诸如[\s\S] 之类的模式 |
(pattern) | 匹配 pattern 并捕获该匹配的子表达式;可以使用 0 ~ 9 属性从结果"匹配"集合中检索捕获的匹配 若要匹配括号字符 ( ),请使用 \( 或者\) |
(?:pattern) | 匹配 pattern 但不捕获该匹配的子表达式,即是一个非捕获匹配,不存储供以后使用的匹配 这对于用 or 字符 ( | ) 组合模式部件的情况有用;例如:`industr(?:y |
(?=pattern) | 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串 是一个非捕获匹配,不能捕获供以后使用的匹配 例如:`Windows (?=95 |
(?!pattern) | 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串 非捕获匹配,即不能捕获供以后使用的匹配 例如:`Windows (?!95 |
x|y | 匹配 x 或 y;例如:`z |
[xyz] | 字符集,匹配包含的任一字符;例如:[abc] 匹配 plain 中的 a |
[^xyz] | 反向字符集,匹配未包含的任何字符;例如:[^abc] 匹配 plain 中 p l i n |
[a-z] | 字符范围,匹配指定范围内的任何字符;例如:[a-z] 匹配 a 到 z 范围内的任何小写字母 |
[^a-z] | 反向范围字符,匹配不在指定的范围内的任何字符;例如:[^a-z] 匹配任何不在 a 到 z 范围内的任何字符 |
\\b | 匹配一个字边界,即字与空格间的位置;例如:er\\b 匹配 never 中的 er ,但不匹 verb 中的 er |
\\B | 非字边界匹配;例如:er\\B 匹配 verb 中的 er ,但不匹配 never 中的 e |
\\cx | 匹配 x 指示的控制字符;例如:\\cM 匹配 Control-M 或回车符x 的值必须在 A-Z 或 a-z 之间;如果不是这样,则假定 c 就是 c 字符本身 |
\\d | 数字字符匹配;等效于 [0-9] |
\\D | 非数字字符匹配;等效于 [^0-9] |
\\s | 匹配任何空白字符,包括单个空格、换行、回车、制表符、换页符等;与[ \\n\\r\\t\\v\\f] 等效 |
\\S | 匹配任何非空白字符;与 [^\\f\\n\\r\\t\\v ] 等效。 |
\\f | 换页符匹配;等效于 \\x0c 和 \\cL |
\\n | 换行符匹配;等效于 \\x0a 和 \\cJ |
\\r | 匹配一个回车符;等效于 \\x0d 和 \\cM |
\\t | 制表符匹配;与 \\x09 和 \\cI 等效 |
\\v | 垂直制表符匹配;与 \\x0b 和 \\cK 等效 |
\\w | 匹配任何字类字符,包括下划线;与[A-Za-z0-9_] 等效 |
\\W | 与任何非单词字符匹配;与[^A-Za-z0-9_] 等效 |
\\xn | 匹配 n,此处的 n 是一个十六进制转义码 十六进制转义码必须正好是两位数长;例如: \\x41 匹配 A;\\x041 与\\x04&1 等效。允许在正则表达式中使用 ASCII 代码 |
\\num | 匹配 num,此处的 num 是一个正整数 到捕获匹配的反向引用;例如: (.)\\1 匹配两个连续的相同字符 |
\\n | 标识一个八进制转义码 或 反向引用; 如果 \\n 前面至少有 n 个捕获子表达式,那么 n 是反向引用;否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码 |
\\nm | 标识一个八进制转义码或反向引用 如果 \\nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用如果 \\nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m如果两种前面的情况都不存在,则 \\nm 匹配八进制值 nm,其中 n 和 m 是八进制数字 (0-7) |
\\nml | 当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml |
\\un | 匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\\u00A9 匹配版权符号 (©) |