Java基础:正则表达式

时间:2022-09-16 14:26:33

相关阅读

  1. Java基础:String类
  2. Java字符串格式化
  3. Java基础:正则表达式

1. 正则表达式概述

正则表达式,又称正规表示法、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本

许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑

1.1 给定一个正则表达式和另一个字符串,我们可以达到如下的目的

  • 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”)
  • 可以通过正则表达式,从字符串中获取我们想要的特定部分

1.2 正则表达式的特点

  • 灵活性、逻辑性和功能性非常的强;
  • 可以迅速地用极简单的方式达到字符串的复杂控制。
  • 对于刚接触的人来说,比较晦涩难懂。

由于正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用,小到著名编辑器EditPlus,大到Microsoft Word、Visual Studio等大型编辑器,都可以使用正则表达式来处理文本内容

2. 正则表达式基础知识

2.1 规则字符在java.util.regex Pattern类中

2.2 常见符号

2.2.1 字符

符号 说明
X 字符X
\\ 反斜线
\t 制表符 (‘\u0009’)
\n 回车
\r 换行
\f 换页符 (‘\u000C’)
\a 报警 (bell) 符 (‘\u0007’)

2.2.2 字符类

符号 说明
[abc] a、b或c
[^abc] 任何字符,除了a、b或c
[a-zA-Z] a到z,或A到Z
[0-9] 0到9的字符
[a-d[m-p]] a到 d或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e或 f(交集)
[a-z&&[^bc]] a到 z,除了 b和 c:[ad-z](减去)
[a-z&&[^m-p]] a到 z,而非 m到 p:[a-lq-z](减去)

2.2.3 预定义字符

符号 说明
. 任何字符
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]

2.3 边界匹配器

符号 说明
^ 行开头
$ 行结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾

2.3.1 Greedy数量词

符号 说明
X? 0次或1次
X* 0次以上
X+ 1次以上
X{n} 恰好n次
X{n,} 至少n次
X{n,m} n-m次

2.3.2 组和捕获

分组可以分为两种形式,捕获组和非捕获组。

捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:

  • 分组0:((A)(B(C)))
  • 分组1:(A)
  • 分组2:(B(C))
  • 分组3:(C)

正则表达式中每个”()”内的部分算作一个捕获组,每个捕获组都有一个编号,从1,2…,编号0代表整个匹配到的内容,组零始终代表整个表达式。

之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用(反向引用) 在表达式中使用,也可以在匹配操作完成后从匹配器检索。

Back引用(\n)是说在后面的表达式中我们可以使用组的编号来引用前面的表达式所捕获到的文本序列。注意:反向引用,引用的是前面捕获组中的文本而不是正则,也就是说反向引用处匹配的文本应和前面捕获组中的文本相同,这一点很重要。

例如 ([” ‘]).*\1 其中使用了分组,\1就是对引号这个分组的引用,它匹配包含在两个引号或者两个单引号中的所有字符串,如,”abc” 或 “’ ” 或’ ” ’ ,但是请注意,它并不会对”a’或者 ‘a”匹配。原因上面已经说明,Back引用只是引用文本而不是表达式。

捕获组的作用就是为了可以在正则表达式内部或者外部(Java方法)引用它。在另一个字符串引用捕获组的内容的方法(“ ") 匹配组的内容。$n用来匹配第n个()里的内容。

利用Matcher中的group(int group)获取捕获组内容。

  • 捕获组命名

如果捕获组的数量非常多,那都用数字进行编号并引用将会非常混乱,并且难以记忆每个捕获组的内容及意义,因此对捕获组命名显得尤为重要;Java 7开始提供了对捕获组命名的语法,并且可以通过捕获组的名称对捕获组反向引用(内外都行)。

命名捕获组的语法格式:(?<自定义名>expr),例如:(?\d{4})-(?\d{2}-(?\d{2}))

有三个命名捕获组year、date和day,从左到右编号分别为1、2、3(编号同样是有效的)

  • 非捕获组

非捕获组,只需要将捕获组中”()”变为”(?:)”即可。

以 (?) 开头的组是纯的非捕获组,它不捕获文本 ,也不针对组合计进行计数。就是说,如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号,因此也不存在Back引用。

我们通过捕获组就能够得到我们想要匹配的内容了,那为什么还要有非捕获组呢?原因是捕获组捕获的内容是被存储在内存中,可供以后使用,比如反向引用就是引用的内存中存储的捕获组中捕获的内容。而非捕获组则不会捕获文本,也不会将它匹配到的内容单独分组来放到内存中。所以,使用非捕获组较使用捕获组更节省内存。在实际情况中我们要酌情选用。

3. 正则表达式的应用

3.1 判断功能

public  boolean matches(String regex):编译给定正则表达式并尝试将给定输入与其匹配。

3.2 分割功能

 public  String[] split(String regex):根据指定的正则表达式分割字符串

3.3 替换功能

public  String replaceAll(String regex,String replacement)

使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。

3.4 获取功能

Pattern和Matcher类的使用

package cn.itcast_05;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* 获取功能
* Pattern和Matcher类的使用
*
* 模式和匹配器的基本使用顺序
*/

public class RegexDemo {
public static void main(String[] args) {
// 模式和匹配器的典型调用顺序
// 把正则表达式编译成模式对象
Pattern p = Pattern.compile("a*b");
// 通过模式对象得到匹配器对象,这个时候需要的是被匹配的字符串
Matcher m = p.matcher("aaaaab");
// 调用匹配器对象的功能
boolean b = m.matches();
System.out.println(b);

//这个是判断功能,但是如果做判断,这样做就有点麻烦了,我们直接用字符串的方法做
String s = "aaaaab";
String regex = "a*b";
boolean bb = s.matches(regex);
System.out.println(bb);
}
}

Pattern 匹配模式

方法声明 功能描述
compile() 把正则表达式编译成匹配模式
matcher() 根据匹配模式去匹配指定的字符串,得到匹配器

Matcher 匹配器

方法声明 功能描述
matches() 匹配字符串
find() 查找有没有满足条件的子串
group() 获取满足条件的子串

3.4 注意事项

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

4. 正则表达式的练习

4.1 判断功能:校验邮箱

package cn.itcast_02;
import java.util.Scanner;
/*
* 校验邮箱
*
* 分析:
* A:键盘录入邮箱
* B:定义邮箱的规则
* 1517806580@qq.com
* liuyi@163.com
* linqingxia@126.com
* fengqingyang@sina.com.cn
* fqy@itcast.cn
* C:调用功能,判断即可
* D:输出结果
*/

public class RegexTest {
public static void main(String[] args) {
//键盘录入邮箱
Scanner sc = new Scanner(System.in);
System.out.println("请输入邮箱:");
String email = sc.nextLine();

//定义邮箱的规则
//String regex = "[a-zA-Z_0-9]+@[a-zA-Z_0-9]{2,6}(\\.[a-zA-Z_0-9]{2,3})+";
String regex = "\\w+@\\w{2,6}(\\.\\w{2,3})+";

//调用功能,判断即可
boolean flag = email.matches(regex);

//输出结果
System.out.println("flag:"+flag);
}
}

4.2 分割功能

代码示例:我有如下一个字符串:”91 27 46 3850”,请写代码实现最终输出结果是:”27 3846 50 91”

package cn.itcast_03;
import java.util.Arrays;
/*
* 我有如下一个字符串:"91 27 46 38 50"
* 请写代码实现最终输出结果是:"27 38 46 50 91"
*
* 分析:
* A:定义一个字符串
* B:把字符串进行分割,得到一个字符串数组
* C:把字符串数组变换成int数组
* D:对int数组排序
* E:把排序后的int数组在组装成一个字符串
* F:输出字符串
*/

public class RegexTest {
public static void main(String[] args) {
// 定义一个字符串
String s = "91 27 46 38 50";

// 把字符串进行分割,得到一个字符串数组
String[] strArray = s.split(" ");

// 把字符串数组变换成int数组
int[] arr = new int[strArray.length];

for (int x = 0; x < arr.length; x++) {
arr[x] = Integer.parseInt(strArray[x]);
}

// 对int数组排序
Arrays.sort(arr);

// 把排序后的int数组在组装成一个字符串
StringBuilder sb = new StringBuilder();
for (int x = 0; x < arr.length; x++) {
sb.append(arr[x]).append(" ");
}
//转化为字符串
String result = sb.toString().trim();

//输出字符串
System.out.println("result:"+result);
}
}

4.3 替换功能:论坛中不能出现数字字符,用*替换

package cn.itcast_04;
/*
* 替换功能
* String类的public String replaceAll(String regex,String replacement)
* 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
*/

public class RegexDemo {
public static void main(String[] args) {
// 定义一个字符串
String s = "helloqq12345worldkh622112345678java";

// 我要去除所有的数字,用*给替换掉
// String regex = "\\d+";
// String regex = "\\d";
//String ss = "*";


// 直接把数字干掉
String regex = "\\d+";
String ss = "";

String result = s.replaceAll(regex, ss);
System.out.println(result);
}
}

4.4 获取功能:获取由三个字符组成的单词

package cn.itcast_05;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* 获取功能:
* 获取下面这个字符串中由三个字符组成的单词
* da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu?
*/

public class RegexDemo2 {
public static void main(String[] args) {
// 定义字符串
String s = "da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu?";
// 规则
String regex = "\\b\\w{3}\\b";

// 把规则编译成模式对象
Pattern p = Pattern.compile(regex);
// 通过模式对象得到匹配器对象
Matcher m = p.matcher(s);
// 调用匹配器对象的功能
// 通过find方法就是查找有没有满足条件的子串
// public boolean find()
// boolean flag = m.find();
// System.out.println(flag);
// // 如何得到值呢?
// // public String group()
// String ss = m.group();
// System.out.println(ss);
//
// // 再来一次
// flag = m.find();
// System.out.println(flag);
// ss = m.group();
// System.out.println(ss);

while (m.find()) {
System.out.println(m.group());
}

// 注意:一定要先find(),然后才能group()
// IllegalStateException: No match found
// String ss = m.group();
// System.out.println(ss);
}
}

5. 正则表达式工具类

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* 正则工具类 提供验证邮箱、手机号、电话号码、身份证号码、数字等方法
*/

public final class RegexUtils {

/**
* 验证Email
*
* @param email
* email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商
* @return 验证成功返回true,验证失败返回false ^ :匹配输入的开始位置。 \:将下一个字符标记为特殊字符或字面值。
* :匹配前一个字符零次或几次。 + :匹配前一个字符一次或多次。 (pattern) 与模式匹配并记住匹配。 x|y:匹配 x 或
* y。 [a-z] :表示某个范围内的字符。与指定区间内的任何字符匹配。 \w :与任何单词字符匹配,包括下划线。
*
* {n,m} 最少匹配 n 次且最多匹配 m 次 $ :匹配输入的结尾。
*/

public static boolean checkEmail(String email) {
String regex = "^(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w{2,3}){1,3})$";
return Pattern.matches(regex, email);
}

/**
* 验证身份证号码
*
* @param idCard
* 居民身份证号码15位或18位,最后一位可能是数字或字母
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkIdCard(String idCard) {
String regex = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}";
return Pattern.matches(regex, idCard);
}

/**
* 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港))
*
* @param mobile
* 移动、联通、电信运营商的号码段
* <p>
* 移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡)
* 、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)
* </p>
* <p>
* 联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)
* </p>
* <p>
* 电信的号段:133、153、180(未启用)、189
* </p>
* <p>
* 虚拟运营商的号段:170
* </p>
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkMobile(String mobile) {
String regex = "(\\+\\d+)?1[34578]\\d{9}$";
return Pattern.matches(regex, mobile);
}

/**
* 验证固定电话号码
*
* @param phone
* 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447
* <p>
* <b>国家(地区) 代码 :</b>标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9
* 的一位或多位数字, 数字之后是空格分隔的国家(地区)代码。
* </p>
* <p>
* <b>区号(城市代码):</b>这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号——
* 对不使用地区或城市代码的国家(地区),则省略该组件。
* </p>
* <p>
* <b>电话号码:</b>这包含从 0 到 9 的一个或多个数字
* </p>
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkPhone(String phone) {
// String regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$";
String regex = "^1\\d{10}$";
return Pattern.matches(regex, phone);
}

/**
* 验证整数(正整数和负整数)
*
* @param digit
* 一位或多位0-9之间的整数
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkDigit(String digit) {
String regex = "\\-?[1-9]\\d+";
return Pattern.matches(regex, digit);
}

/**
* 验证整数和浮点数(正负整数和正负浮点数)
*
* @param decimals
* 一位或多位0-9之间的浮点数,如:1.23,233.30
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkDecimals(String decimals) {
String regex = "\\-?[1-9]\\d+(\\.\\d+)?";
return Pattern.matches(regex, decimals);
}

/**
* 验证空白字符
*
* @param blankSpace
* 空白字符,包括:空格、\t、\n、\r、\f、\x0B
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkBlankSpace(String blankSpace) {
String regex = "\\s+";
return Pattern.matches(regex, blankSpace);
}

/**
* 验证中文
*
* @param chinese
* 中文字符
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkChinese(String chinese) {
String regex = "^[\u4E00-\u9FA5]+$";
return Pattern.matches(regex, chinese);
}

/**
* 验证日期(年月日)
*
* @param birthday
* 日期,格式:1992-09-03,或1992.09.03
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkBirthday(String birthday) {
String regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}";
return Pattern.matches(regex, birthday);
}

/**
* 验证URL地址
*
* @param url
* 格式:http://blog.csdn.net:80/xyang81/article/details/7705960? 或
* http://www.csdn.net:80
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkURL(String url) {
String regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?";
return Pattern.matches(regex, url);
}

/**
* 匹配中国邮政编码
*
* @param postcode
* 邮政编码
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkPostcode(String postcode) {
String regex = "[1-9]\\d{5}";
return Pattern.matches(regex, postcode);
}

/**
* 匹配IP地址(简单匹配,格式,如:192.168.1.1,127.0.0.1,没有匹配IP段的大小)
*
* @param ipAddress
* IPv4标准地址
* @return 验证成功返回true,验证失败返回false
*/

public static boolean checkIpAddress(String ipAddress) {
String regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))";
return Pattern.matches(regex, ipAddress);
}

public static boolean checkNickname(String nickname) {
String regex = "^[a-zA-Z0-9\u4E00-\u9FA5_]+$";
return Pattern.matches(regex, nickname);
}


public static boolean hasCrossSciptRiskInAddress(String str) {
String regx = "[`~!@#$%^&*+=|{}':;',\\[\\].<>~!@#¥%……&*——+|{}【】‘;:”“’。,、?-]";
if (str != null) {
str = str.trim();
Pattern p = Pattern.compile(regx, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
return m.find();
}
return false;
}
}