目录
2.1.3replaceFirst 和 replaceAll 方法
2.2PatternSyntaxException 类的方法
1.java正则表达式
正则表达式定义了字符串的模式。
正则表达式可以用来搜索、编辑或处理文本
Java 提供了 java.util.regex 包,它包含了 Pattern 和 Matcher 类,用于处理正则表达式的匹配操作。
正则表达式实例
一个字符串其实就是一个简单的正则表达式,例如 Hello World 正则表达式匹配 "Hello World" 字符串。
.(点号)也是一个正则表达式,它匹配任何一个字符如:"a" 或 "1"。
2.java.util.regex包
java.util.regex 包是 Java 标准库中用于支持正则表达式操作的包。
java.util.regex 包主要包括以下三个类:
- Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
- Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
- PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误
以下实例中使用了正则表达式 .*runoob.* 用于查找字符串中是否包了 runoob 子串:
import java.util.regex.Pattern;
public class Test66 {
public static void main(String[] args) {
//先写个字符串
String content="I am noob "+"from runoob.com.";
System.out.println(content);
//定义正则表达式规则
String pattern= ".*runoob.*";
boolean isMatch= Pattern.matches(pattern,content);
System.out.println("字符串中是否包含了 'runoob' 子字符串? " + isMatch);
}
}
2.1Matcher类
2.1.1索引方法start和end方法
索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test33 {
public static void main(String[] args) {
String str="cat cat cat cattie cat";
String patte1="\\bcat\\b";
Pattern p=Pattern.compile(patte1);
Matcher m=p.matcher(str);
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());
}
}
}
2.1.2matches 和 lookingAt 方法
matches 和 lookingAt 方法都用来尝试匹配一个输入序列模式。它们的不同是 matches 要求整个序列都匹配,而lookingAt 不要求。
lookingAt 方法虽然不需要整句都匹配,但是需要从第一个字符开始匹配。
这两个方法经常在输入字符串的开始使用。
我们通过下面这个例子,来解释这个功能:
String s1="foo";
String s2="fooooooooooooooo";
String s3="oooooofoooo";
Pattern p=Pattern.compile(s1);
Matcher m1=p.matcher(s2);
Matcher m2=p.matcher(s3);
System.out.println(m1.lookingAt());//true
System.out.println(m2.lookingAt());//false
System.out.println(m1.matches());//false
System.out.println(m2.matches());//false
2.1.3replaceFirst 和 replaceAll 方法
replaceFirst 和 replaceAll 方法用来替换匹配正则表达式的文本。不同的是,replaceFirst 替换首次匹配,replaceAll 替换所有匹配。
下面的例子来解释这个功能:
2.2PatternSyntaxException 类的方法
PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。
PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。
3.正则表达式语法
3.1普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
3.1.1字符类
1.
//一个[]表示一个字符
2.
// 不能出现a b c , ^表示取反的意思
3.
// a到zA到Z(包括头尾的范围) , -写在[]里面表示范围的意思,写在[]外面表示就是一个-
// 第一个z跟第一个[]去匹配 第二个z跟第二个[]去匹配
4.
// [a-d[m-p]] a到d,或m到p
5.
// [a-z&&[def]] a-z和def的交集。为:d,e,f // 细节:如果要求两个范围的交集,那么需要写符号&& // 如果写成了一个&,那么此时&表示就不是交集了,而是一个简简单单的&符号,没有任何含义
6.[a-z&&[^bc]] a-z之间排除了b,c a-z和非bc的交集。(等同于[ad-z])
7.[a-z&&[^m-p]] a到z和除了m到p的交集。(等同于[a-lq-z])
//public boolean matches(String regex):判断是否与正则表达式匹配,匹配返回true
//一个[]表示一个字符
System.out.println("----------1------------");
System.out.println("a".matches("[abc]"));//true
System.out.println("z".matches("[abc]"));//false
// 一个[]表示一个字符,第二个b没有什么东西跟它去匹配,所以不满足为false
System.out.println("ab".matches("[abc]"));//false
System.out.println("ab".matches("[abc][abc]"));//true
// 不能出现a b c , ^表示取反的意思
System.out.println("------------2------------");
System.out.println("a".matches("[^abc]"));//false
System.out.println("z".matches("[^abc]"));//true
System.out.println("zz".matches("[^abc]"));//false
System.out.println("zz".matches("[^abc][^abc]"));//true
// a到zA到Z(包括头尾的范围) , -写在[]里面表示范围的意思,写在[]外面表示就是一个-
System.out.println("---------------3---------------");
System.out.println("a".matches("[a-zA-Z]"));//true
System.out.println("z".matches("[a-zA-Z]"));//true
System.out.println("aa".matches("[a-zA-Z]"));//false
System.out.println("zz".matches("[a-zA-Z]"));//false
// 第一个z跟第一个[]去匹配 第二个z跟第二个[]去匹配
System.out.println("zz".matches("[a-zA-Z][a-zA-Z]"));//true
System.out.println("0".matches("[a-zA-Z]"));//false
System.out.println("0".matches("[a-zA-Z0-9]"));//true
System.out.println("0".matches("[0-9]"));//true
// [a-d[m-p]] a到d,或m到p
System.out.println("-----------4-------------");
System.out.println("a".matches("[a-d[m-p]]"));//true
System.out.println("d".matches("[a-d[m-p]]"));//true
System.out.println("m".matches("[a-d[m-p]]"));//true
System.out.println("p".matches("[a-d[m-p]]"));//true
System.out.println("e".matches("[a-d[m-p]]"));//false
System.out.println("0".matches("[a-d[m-p]]"));//false
// [a-z&&[def]] a-z和def的交集。为:d,e,f
// 细节:如果要求两个范围的交集,那么需要写符号&&
// 如果写成了一个&,那么此时&表示就不是交集了,而是一个简简单单的&符号,没有任何含义
System.out.println("-----------5---------------");
System.out.println("a".matches("[a-z&[def]]"));//true
System.out.println("&".matches("[a-z&[def]]"));//true
System.out.println("d".matches("[a-z&[def]]"));//true
System.out.println("0".matches("[a-z&[def]]"));//false
System.out.println("a".matches("[a-z&&[def]]")); //false
System.out.println("&".matches("[a-z&&[def]]")); //false
System.out.println("d".matches("[a-z&&[def]]")); //true
System.out.println("0".matches("[a-z&&[def]]")); //false
// [a-z&&[^bc]] a-z之间排除了b,c a-z和非bc的交集。(等同于[ad-z])
System.out.println("---------6-------------");
System.out.println("a".matches("[a-z&&[^bc]]"));//true
System.out.println("b".matches("[a-z&&[^bc]]")); //false
System.out.println("0".matches("[a-z&&[^bc]]")); //false
// [a-z&&[^m-p]] a到z和除了m到p的交集。(等同于[a-lq-z])
System.out.println("-----------7-------------");
System.out.println("a".matches("[a-z&&[^m-p]]")); //true
System.out.println("m".matches("[a-z&&[^m-p]]")); //false
System.out.println("0".matches("[a-z&&[^m-p]]")); //false
3.1.2预定义字符
正则表达式的一些常见匹配规则, \ 转义字符 改变后面那个字符原本的含义
练习1
以字符串的形式打印一个双引号,双引号"在Java中表示字符串的开头或者结尾,此时\表示转义字符,改变了后面那个双引号原本的含义,把他变成了一个普普通通的双引号而已
练习2
\表示转义字符 // 这个\转义了后面的U 这个/转义了后面的m,System.out.println("c:\Users\moon\IdeaProjects\\basic-code\\myapi\\src"); //两个\的理解方式:前面的\是一个转义字符,改变了后面\原本的含义,把他变成一个普普通通的\而已。
1.
//.表示任意一个字符 一个.只能表示一个字符 // 两个.表示前面需要有两个任意的字符
2.
// \\d 表示任意的一个数字 // \\d只能是任意的一位数字 // 简单来记:在Java当中两个\表示一个\,因为一个\有特殊含义,表示转义字符
3.
//\\w,小写w 只能是一位单词字符[a-zA-Z_0-9] 可以是字母、数字或者下划线
4.
// 非单词字符 大写W
5.\S \s
注意:单个字符与预定义字符不能混淆写
例如
输出结果是个false
3.2非打印字符
非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:
3.3特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如上面说的 runoo*b 中的 *,简单的说就是表示任何字符串的意思。如果要查找字符串中的 * 符号,则需要对 * 进行转义,即在其前加一个 \,runo\*ob 匹配字符串 runo*ob。
许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符\ 放在它们前面。下表列出了正则表达式中的特殊字符
3.4限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。
正则表达式的限定符有:
// 必须是数字 字母 下划线 至少 6位
System.out.println("------------5-----------");
System.out.println("123321".matches("\\w{6,}"));//true
System.out.println("47821jd_fadf".matches("\\w{6,}"));//true
System.out.println("h12j".matches("\\w{6,}"));//false
// 必须是数字和字符 必须是4位
System.out.println("------6------------");
System.out.println("kasf".matches("\\w{4}"));//true
System.out.println("jkfasfh".matches("\\w{4}"));//false
System.out.println("shai".matches("[0-9a-zA-Z]{4}"));//true
System.out.println("23dF".matches("[a-zA-Z0-9]{4}"));//true
System.out.println("23 F".matches("[a-zA-Z0-9]{4}"));//false
System.out.println("adbc".matches("[\\w&&[^_]]{4}"));//true
System.out.println("23dF".matches("[\\w&&[^_]]{4}"));//true
System.out.println("23_F".matches("[\\w&&[^_]]{4}"));//false
3.4.1贪婪与非贪婪
* 和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配
例如,您可能搜索 HTML 文档,以查找在 h1 标签内的内容。HTML 代码如下:
贪婪:下面的表达式匹配从开始小于符号 (<) 到关闭 h1 标记的大于符号 (>) 之间的所有内容。
非贪婪:如果您只需要匹配开始和结束 h1 标签,下面的非贪婪表达式只匹配 <h1>。
通过在 *、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配
String s1="<h1>RUNOOB-菜鸟教程</h1>";
//1.定义爬取规则
//贪婪
String regex="<.*>";
//非贪婪
String regex2="<.*?>";
//编译正则表达式成为一个匹配规则对象
Pattern pattern=Pattern.compile(regex);
//通过匹配规则对象得到一个匹配数据的内容的匹配器对象
Matcher matcher=pattern.matcher(s1);
while(matcher.find()){
System.out.println(matcher.group());
}
System.out.println("----------------");
//非贪婪
Pattern pattern1=Pattern.compile(regex2);
Matcher matcher1=pattern1.matcher(s1);
while(matcher1.find()){
System.out.println(matcher1.group());
}
3.5定位符
定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。
定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
正则表达式的定位符有:
注意:不能将限定符与定位符一起使用。由于在紧靠换行或者单词边界的前面或后面不能有一个以上位置,因此不允许诸如 ^* 之类的表达式。
若要匹配一行文本开始处的文本,请在正则表达式的开始使用 ^ 字符。不要将 ^ 的这种用法与中括号表达式内的用法混淆。
若要匹配一行文本的结束处的文本,请在正则表达式的结束处使用 $ 字符。
1.若要在搜索章节标题时使用定位点,下面的正则表达式匹配一个章节标题,该标题只包含两个尾随数字,并且出现在行首:
2.正的章节标题不仅出现行的开始处,而且它还是该行中仅有的文本。它既出现在行首又出现在同一行的结尾。下面的表达式能确保指定的匹配只匹配章节而不匹配交叉引用。通过创建只匹配一行文本的开始和结尾的正则表达式,就可做到这一点。
3.匹配单词边界稍有不同,但向正则表达式添加了很重要的能力。单词边界是单词和空格之间的位置。非单词边界是任何其他位置。下面的表达式匹配单词 Chapter 的开头三个字符,因为这三个字符出现在单词边界后面:
4.\b 字符的位置是非常重要的。如果它位于要匹配的字符串的开始,它在单词的开始处查找匹配项。如果它位于字符串的结尾,它在单词的结尾处查找匹配项。例如,下面的表达式匹配单词 Chapter 中的字符串 ter,因为它出现在单词边界的前面:
5.下面的表达式匹配 Chapter 中的字符串 apt,但不匹配 aptitude 中的字符串 apt:
6.字符串 apt 出现在单词 Chapter 中的非单词边界处,但出现在单词 aptitude 中的单词边界处。对于 \B 非单词边界运算符,不可以匹配单词的开头或结尾,如果是下面的表达式,就不匹配 Chapter 中的 Cha:
3.6选择
用圆括号 () 将所有选择项括起来,相邻的选择项之间用 | 分隔。
() 表示捕获分组,() 会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(n 是一个数字,表示第 n 个捕获组的内容)。
String s9="123456runood123runood456";
//定义爬取规则
String n="([0-9])([a-z]+)";
Pattern p=Pattern.compile(n);
Matcher m=p.matcher(s9);
int count=0;
while(m.find()){
count++;
System.out.println(m.group());
}
System.out.println(count);
但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在第一个选项前来消除这种副作用。
其中 ?: 是非捕获元之一,还有两个非捕获元是 ?= 和 ?!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。
以下列出 ?=、?<=、?!、?<! 的使用区别
3.7反向引用
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 \n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
可以使用非捕获元字符 ?:、?= 或 ?! 来重写捕获,忽略对相关匹配的保存。
反向引用的最简单的、最有用的应用之一,是提供查找文本中两个相同的相邻单词的匹配项的能力。以下面的句子为例:
3.8捕获组
捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。
例如,正则表达式 (dog) 创建了单一分组,组里包含"d","o",和"g"。
捕获组是通过从左至右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:
- ((A)(B(C)))
- (A)
- (B(C))
- (C)
可以通过调用 matcher 对象的 groupCount 方法来查看表达式有多少个分组。groupCount 方法返回一个 int 值,表示matcher对象当前有多个捕获组。
还有一个特殊的组(group(0)),它总是代表整个表达式。该组不包括在 groupCount 的返回值中。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
public static void main( String[] args ){
// 按指定模式在字符串查找
String line = "This order was placed for QT3000! OK?";
String pattern = "(\\D*)(\\d+)(.*)";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
System.out.println("Found value: " + m.group(2) );
System.out.println("Found value: " + m.group(3) );
} else {
System.out.println("NO MATCH");
}
}
}
三行代码 str.match(patt1) 返回一个数组,实例中的数组包含 5 个元素,索引 0 对应的是整个字符串,索引 1 对应第一个匹配符(括号内),以此类推。
第一个括号子表达式捕获 Web 地址的协议部分。该子表达式匹配在冒号和两个正斜杠前面的任何单词。
第二个括号子表达式捕获地址的域地址部分。子表达式匹配非 : 和 / 之后的一个或多个字符。
第三个括号子表达式捕获端口号(如果指定了的话)。该子表达式匹配冒号后面的零个或多个数字。只能重复一次该子表达式。
最后,第四个括号子表达式捕获 Web 地址指定的路径和 / 或页信息。该子表达式能匹配不包括 # 或空格字符的任何字符序列。
将正则表达式应用到上面的 URI,各子匹配项包含下面的内容:
- 第一个括号子表达式包含 https
- 第二个括号子表达式包含 www.runoob.com
- 第三个括号子表达式包含 :80
- 第四个括号子表达式包含 /html/html-tutorial.html
3.9修饰符
以下是一些常用的修饰符及其功能:
-
CASE_INSENSITIVE
(忽略大小写)- 使匹配不区分字母的大小写。
- 用法示例:
Pattern.compile("abc", Pattern.CASE_INSENSITIVE)
-
MULTILINE
(多行模式)- 将
^
和$
的匹配模式扩展到每一行的开始和结束,而不仅仅是整个输入字符串的开始和结束。 - 用法示例:
Pattern.compile("abc", Pattern.MULTILINE)
- 将
-
DOTALL
(点任意匹配)- 扩展
.
(点)字符的匹配范围,使其可以匹配包括换行符在内的所有字符。 - 用法示例:
Pattern.compile("a.b", Pattern.DOTALL)
- 扩展
在 Java 的正则表达式中,标准的正则表达式并没有提供 “全局匹配” 的修饰符。在 Javascript 和某些其他语言中,可能会有一个全局标志(如 g
)
在 Java 中进行全局匹配
在 Java 中,要进行全局匹配,你通常会使用 Matcher
类的 find()
方法。这个方法可以用来在输入字符串中查找所有匹配的子字符串。你可以在一个 while
循环中反复调用 find()
方法,以找到所有的匹配
// 使用 find() 方法查找所有匹配项
while (matcher.find()) {
System.out.println("匹配的内容: " + matcher.group());
}
4.正则表达式优先级
正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。
相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:
以下是一些常见正则表达式运算符按照优先级从高到低的顺序:
-
转义符号:
\
是用于转义其他特殊字符的转义符号。它具有最高的优先级。示例:
\d
、\.
等,其中\d
匹配数字,\.
匹配点号。 -
括号: 圆括号
()
用于创建子表达式,具有高于其他运算符的优先级。示例:
(abc)+
匹配 "abc" 一次或多次。 -
量词: 量词指定前面的元素可以重复的次数。
示例:
a*
匹配零个或多个 "a"。 -
字符类: 字符类使用方括号
[]
表示,用于匹配括号内的任意字符。示例:
[aeiou]
匹配任何一个元音字母。 -
断言: 断言是用于检查字符串中特定位置的条件的元素。
示例:
^
表示行的开头,$
表示行的结尾。 -
连接: 连接在没有其他运算符的情况下表示字符之间的简单连接。
示例:
abc
匹配 "abc"。 -
管道: 管道符号
|
表示"或"关系,用于在多个模式之间选择一个。示例:
cat|dog
匹配 "cat" 或 "dog"。
5.正则表达式匹配规则
基本模式匹配
一切从最基本的开始。模式,是正则表达式最基本的元素,它们是一组描述字符串特征的字符。模式可以很简单,由普通的字符串组成,也可以非常复杂,往往用特殊的字符表示一个范围内的字符、重复出现,或表示上下文。例如
这个模式包含一个特殊的字符 ^,表示该模式只匹配那些以 once 开头的字符串。例如该模式与字符串 "once upon a time" 匹配,与 "There once was a man from NewYork" 不匹配。正如如 ^ 符号表示开头一样,$ 符号用来匹配那些以给定模式结尾的字符串
这个模式与 "Who kept all of this cash in a bucket" 匹配,与 "buckets" 不匹配。字符 ^ 和 $ 同时使用时,表示精确匹配(字符串与模式一样)。例如:
只匹配字符串 "bucket"。如果一个模式不包括 ^ 和 $,那么它与任何包含该模式的字符串匹配。例如模式:
与字符串
是匹配的
4.实例
4.1找出字符串中的邮箱编码
4.2中文正则表达式
[\\u4E00-\\u9FA5]
4.3车牌号正则表达式
[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}
4.4微信号正则表达式
微信号正则表达式,6至20位,以字母开头,字母,数字,减号,下划线
[a-zA-Z][-_a-zA-Z0-9]{5,19}
分析:
^:匹配的开始 [a-zA-Z]:字母不区分大小写 ^[a-zA-Z]:首字母不区分大小写 [-_a-zA-Z0-9]:字母、数字、下划线和减号 {5,19}:6-20位
4.5QQ号正则表达式
[1-9][0-9]{4,10}
4.6十六进制颜色正则表达式
^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$
4.7密码强度正则表达式
最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符
^.*(?=.{6,})(?=.*\\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$