Java
正则表达式
正则表达式可以校验字符串是否满足一定的规则, 并用来校验数据的合法性
- 校验字符产是否满足规则
- 在一段文本总查找满足要求的内容
java
public class Test4 {
public static void main(String[] args) {
//要求: 个数在6-20之间, 第一位不能为0
String qq = "1234567890";
boolean flag = checkQQ(qq);
System.out.println(flag);
}
//校验数据
public static boolean checkQQ(String qq) {
int qqLen = qq.length();
if (qqLen <= 6 || qqLen >= 20) return false;
//第一位为0
if (qq.startsWith("0")) return false;
return true;
}
}如果校验过于复杂就可使用正则表达式
- 使用正则表达式校验数据
java
public class Test4 {
public static void main(String[] args) {
//要求: 个数在6-20之间, 第一位不能为0
String qq = "1234567890";
//matches的参数式正则表达式规则
boolean flag = qq.matches("[1-9]\\d{5,20}");
System.out.println(flag);
}
}字符类
| 字符类(只匹配一个字符) | 描述 |
|---|---|
[abc] | 只能是a,b,c |
[^abc] | 不能是a,b,c, 相当于取反 |
[a-zA-Z] | a-z, A-Z |
[a-d[m-p]] | a到d或者m到p, 和上一个差不多 |
[a-z&&[def]] | a-z和def的交集, 为d, e, f |
[a-z&&[^bc]] | a-z和非bc的交集, 为a, d-z 相当于:[ad-z] |
[a-z&&[^m-p]] | a-z和除了m-p的交集, 为a-l, q-z 相当于:[a-lq-z] |
[]在此不是数组, 而是表示一个范围
预定义字符
| 预定义字符(只匹配一个字符) | 描述 |
|---|---|
. | 任意字符 |
\d | 数字: [0-9] |
\D | 非数字[^0-9] |
\s | 空白字符: [\t\n\x0B\f\r] |
\S | 非空白字符: [^\s] |
\w | 字母数字下划线: [a-zA-Z_0-9] |
\W | 非字母数字下划线: [^\w] |
转义字符
| 转义字符 | 描述 |
|---|---|
\ | 转义字符, 作用: 改变后面一个字符原本的意义 |
java
public class Test4 {
public static void main(String[] args) {
//在window下文件路径一般只有一个\, 而\在java中有特殊意义, 所以需要转义
String path = "D:\\JAVA文件\\java_study\\puzzle-game\\image\\about.png";
}
}量词
| 量词(匹配前面的字符多次) | 描述 |
|---|---|
X* | X, 出现0次或多次 |
X+ | X, 出现1次或多次 |
X? | X, 出现0次或1次 |
X{n} | X, 出现n次 |
X{n,} | X, 出现n次或多次 |
X{n,m} | X, 出现n到m次 |
逻辑运算符
| 逻辑运算符 | 描述 |
|---|---|
| | 或 |
() | 括号将规则组合 |
&& | 逻辑与 |
[abc] | a或b或c |
忽略大小写
(?i)-- 忽略大小写
java
//忽略abc的大小写
String regex4 = "(?i)abc";
System.out.println("abc".matches(regex4));// true
System.out.println("Abc".matches(regex4));// true
System.out.println("aBC".matches(regex4));// true
//忽略bc的大小写
String regex5 = "a(?i)bc";
System.out.println("abc".matches(regex5));// true
System.out.println("Abc".matches(regex5));// false
System.out.println("aBC".matches(regex5));// true
//只忽略b的大小写, 就是用括号单独括起来就行
String regex6 = "a((?i)b)c";
System.out.println("abc".matches(regex6));// true
System.out.println("Abc".matches(regex6));// false
System.out.println("aBC".matches(regex6));// false综合练习
java
public class Demo1 {
public static void main(String[] args) {
/*
* 请编写正则表达式验证用户输入的手机号码是否满足要求。
请编写正则表达式验证用户输入的邮箱号是否满足要求。
请编写正则表达式验证用户输入的电话号码是否满足要求。
验证手机号码 13112345678 13712345667 13945679027 139456790271
验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434
验证邮箱号码 3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn
* */
/*心得: 编写正则不要以全局的方式看待, 而是局部的从左到右*/
//第一部分 1 即第一个字符为1
//第二部分 [3-9] 即第二个字符为3到9之间的数字
//第三部分 \\d{9} 即匹配9个数字
String regex1 = "1[3-9]\\d{9}";
System.out.println("13112345678".matches(regex1));// true
System.out.println("13712345667".matches(regex1));// true
System.out.println("13945679027".matches(regex1));// true
System.out.println("139456790271".matches(regex1));// false
System.out.println("---------------------------------");
//1. 区号 0\\d{2,3}
//1.1 首字符为0; 剩余的数字是随机的, 可能是2位, 可能是3位
//2. - -?
//2.1 '-'只存在一次或0次 ?表示0次或者1次
//3. 座机号 第一位不能是0, 其余可以是任何数字, 号码总长度: 5-10位 [1-9]\d{4,9}
String regex2 = "0\\d{2,3}-?[1-9]\\d{4,9}";
System.out.println("020-2324242".matches(regex2));
System.out.println("02122442".matches(regex2));
System.out.println("027-42424".matches(regex2));
System.out.println("0712-3242434".matches(regex2));
System.out.println("---------------------------------");
String regex3 = "\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}";
System.out.println("3232323@qq.com".matches(regex3));
System.out.println("zhangsan@itcast.cnn".matches(regex3));
System.out.println("dlei0009@163.com".matches(regex3));
System.out.println("dlei0009@pci.com.cn".matches(regex3));
//严格验证身份证信息 420704 20031028 153 X
//1. 市区 省份 派出所 第一位不可以为0 2-5为任意数字 [1-9]\\d{5}
//2. 出身年月日 20 03 10 28
//2.1 年的前半部分 有身份证的都 18 19 20年 (18|19|20)
//2.2 年的后半部分 2位任意数字 \\d{2}
//3. 月份 1 ~ 12 (0[1-9]|1[0-2])
//4. 日 1- 31 ==> 01 02 10 20 30 31 (0[1-9]|[12]\\d|3[01])
//5. 153位任意3位数字 \\d{3}
//6. 数字|x | X 最后一位可以是数字或者大小写x [0-9xX]
String strictIdRule = "[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9xX]";
System.out.println("42070420031028153X".matches(strictIdRule));
System.out.println("02070420031028153X".matches(strictIdRule));
System.out.println("42070420031028153x".matches(strictIdRule));
System.out.println("42070417351028153X".matches(strictIdRule));
}
}本地爬虫和网络爬虫
- 本地爬虫:从本地文件读取数据,例如:从本地文件读取HTML页面,解析HTML页面,提取数据。
- 网络爬虫:从互联网上获取数据,例如:从互联网上获取HTML页面,解析HTML页面,提取数据。
java
//本地爬虫
public class Demo2 {
public static void main(String[] args) {
String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
//获取正则表达式对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//文本匹配器对象
Matcher m = p.matcher(str);
//m.find()会拿着文本匹配器从头开始读取, 寻找有满足规则的子串
//如果没有返回false
//如果有, 返回true, 并在底层记录子串的起始索引和结束索引+1
while (m.find()) {
//m.group()会根据find方法记录的索引进行字符串截取
//使用substring(start, end); 最终会把子串返回
String group = m.group();
System.out.println(group);
}
}
}最终输出
Java Java8 Java11 Java17 Java17
网络爬虫
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Pattern;
public class Demo3 {
public static void main(String[] args) throws IOException {
//网络爬取
URL url = new URL("https://cn.vuejs.org/");
URLConnection con = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String regex = "[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9xX]";
String line;
Pattern p = Pattern.compile(regex);
while ((line = br.readLine()) != null) {
// Matcher m = p.matcher(line);
// System.out.println(m.group());
System.out.println(line);
}
}
}条件爬取
?=-- 配合占位符,表示后面必须匹配才满足条件,例如:(?=8|11|17)?:--((?i)java)(8|11|17)就相当于((?i)java)(?:8|11|17)?!-- 表示后面不能匹配才行,例如:(?!8|11|17)
- 需求1: 爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。
- 需求2: 爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17
- 需求3: 爬取除了版本号为8,11.17的Java文本,
java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Demo5 {
public static void main(String[] args) {
String s = "java自从95年问世以来,经历了很多版本,目前企业中用的最多的是JAva8和JavA11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久JAva17也会逐渐登上历史舞台";
// ?=8|11|17中的 ? 相当于是占位符
String regex = "((?i)java)(?=8|11|17)";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
while (m.find()) {
//最终取得过程中不会显示Java后面的数字, 但Java后面带数字是条件, 例如第一个Java后面没有数字就不符合条件
System.out.println(m.group());
}
}
/*
最终输出:
JAva
JavA
Java
JAva
*/
}java
String regex2 = "((?i)java)(8|11|17)";
String regex3 = "(?i)java(?:8|11|17)";//与regex2功能相同java
//匹配java后面没有8|11|17的位置
String regex4 = "((?i)java)(?!8|11|17)";贪婪爬取与非贪婪爬取
- 贪婪爬取: 在爬取数据的时候尽可能的多获取数据
- 非贪婪爬取: 在爬取数据的时候尽可能的少获取数据
java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Demo6 {
public static void main(String[] args) {
String s = "Java自从95年问世以来,abbbbbbbbbbbbaaaaaaaaaaaaaaaaaa" +
"经历了很多版木,目前企业中用的最多的是]ava8和]ava11,因为这两个是长期支持版木。" +
"下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
//贪婪爬取, 尽可能的多获取数据
Pattern p = Pattern.compile("ab+");
//非贪婪爬取, 在之前的基础上添加一个问号
// Pattern p = Pattern.compile("ab+?");
Matcher m = p.matcher(s);
while(m.find()) {
System.out.println(m.group());
//abbbbbbbbbbbb
}
}
}正则表达式在字符串方法中的使用
| 方法名 | 描述 |
|---|---|
boolean matches(String regex) | 如果此字符串与给定的正则表达式匹配,则返回true |
String replaceAll(String regex, String replacement) | 使用给定的替换字符串替换所有匹配给定正则表达式的字符串 |
String[] split(String regex) | 根据匹配给定的正则表达式来拆分此字符串 |
方法使用
java
public class Demo6 {
public static void main(String[] args) {
String s2 = "小小汪abc123大大王ab123c励志汪";
//匹配字至少一次
String newStr = s2.replaceAll("[0-9a-zA-z]+", "vs");
System.out.println(newStr);// 小小汪vs大大王vs励志汪
//根据匹配给定的正则表达式来拆分此字符串
String[] split = newStr.split("\\w+");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
/*
小小汪
大大王
励志汪
*/
}
}分组
分组就是使用括号()括起来的部分
捕获分组
- 语法格式 ==>
\\元组位置 - 作用: 把一组的数据捕获出来, 再用一次. 注: 是数据, 不是正则表达式的规则再用一次
- 判断捕获分组的规则
- 从
1开始, 连续不间断 - 以左括号为基准, 最左边的是第一组, 其次是第二组, 以此类推
- 从
java
//需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
//举例: a123a b456b 17891 &abc& a123b(false)
//(.)为第一i个元组, 而之后的\\1就是把(.)匹配的数据拿过来再用一次
String regex1 = "(.).+\\1";
System.out.println("a123a".matches(regex1));
System.out.println("b456b".matches(regex1));
System.out.println("17891".matches(regex1));
System.out.println("&abc&".matches(regex1));
System.out.println("a123b".matches(regex1));
//需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
//举例: abc123abc b456b 123789123 &!@abc&!@ abc123abd(false)
String regex2 = "(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
System.out.println("b456b".matches(regex2));
System.out.println("123789123".matches(regex2));
System.out.println("&!@abc&!@".matches(regex2));
System.out.println("abc123abd".matches(regex2));
System.out.println("--------------------------------");
//需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
//举例: aaa123aaa bbb456bbb 111789111 &&abc&&
String regex3 = "((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex2));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("111789112".matches(regex3));解析((.)\\2*).+\\1
根据捕获分组的规则, 以左括号为基准, 最左边的是第一组, 其次是第二组, 以此类推.
- 这里的
\\1指的是((.)\\2*)这一个整体 \\2指的是(.)
使用分组的情况
如果想要后续使用本组的数据, 需由两个注意点
- 正则内部使用:
\\组号 - 正则外部使用:
$组号
java
String str = "我要学学编编编编程程程程程程";
//需求:把重复的内容 替换为 单个的
//学学 学
//编编编编 编
//程程程程程程 程
//result: 我要学编程
// (.)表示任意字符, \\1表示第一个元组匹配至少一次 例: 当.匹配到"学"的时候, 那么\\1也表示"学", 最后会替换为$1也就是第一个元组,
//而此时一个元组的数据就是学, 最终就把 "学学" ==> "学"
String result = str.replaceAll("(.)\\1+", "$1");//使用$外部引用元组的数据
System.out.println(result);非捕获分组
分组之后不需要再用本组数据, 仅仅把数据括起来
| 符号 | 含义 | 例子 |
|---|---|---|
(?:正则) | 获取所有 | Java(?:8|11|17) |
(?=正则) | 获取前面的部分 | Java(?:8|11|17) |
(?!正则) | 获取不是指定内容的前面 | Java(?:8|11|17) |
java
//需求:判断一个字符串是否是合法的身份证号
String regex1 ="[1-9]\\d{16}(\\d|x|X)";
System.out.println("42070420031028153X".matches(regex1));
// [1-9]\\d{16}(\\d|x|X)中的规则, (\\d|x|X)如果只需要使用一次就可以在括号内添加?:
String regex2 ="[1-9]\\d{16}(?:\\d|x|X)";
System.out.println("42070420031028153X".matches(regex2));// true
//报错: 因为使用?:括起来的元组不可以算作捕获元组
String regex1 ="[1-9]\\d{16}(?:\\d|x|X)\\1";
System.out.println("42070420031028153X".matches(regex1));注意
()里面使用了?:, 那么就不可以捕获此元组的数据