1.正则表达式
正则表达式可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性
初体验正则表达式
public class RegexDemo1 {
public static void main(String[] args) {
/*校验qq是否正确
* 规则:6~15位之间,不能0开头,必须全是数字*/
String qq = "2426580778";
boolean result = checkQQ(qq);
System.out.println(result);
//正则表达式
System.out.println(qq.matches("[1-9]\\d{5,14}"));
}
//核心思想:先把异常数据进行过滤,剩下的就是满足要求的数据
public static boolean checkQQ(String qq){
//6~15位之间
int len = qq.length();
if(len > 15 || len < 6){
return false;
}
//不能0开头
char ch = qq.charAt(0);
if(ch == '0'){
return false;
}
//都是数字
for (int i = 0; i < qq.length(); i++) {
char c = qq.charAt(i);
if(c < '0' || c > '9'){
return false;
}
}
return true;
}
}
正则表达式的作用:
- 校验字符串是否满足规则
- 在一段文本中查找满足要求的内容
正则表达式规则:
[abc] | 只能是a,或b,或c |
[^abc] | 除了a,b,c之外的任何字符 |
[a-zA-Z] | a到zA到Z,包括(范围) |
[a-d[m-p]] | a到d,或m到p |
[a-z&&[def]] | a到z和def的交集。为:d,e,f |
[a-z&&[^bc]] | a到z和非bc的交集。(等同于[ad-z]) |
[a-z&&[^m-p]] | a到z和非m到p的交集。(等同于[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]一个非单词字符 |
public static void main(String[] args) {
// \ 表示转义字符 改变后面那个字符原本含义
// " 在Java中表示字符串的开头或结尾
// 此时\表示转义字符,改变后面那个双引号原本含义
//所以才能以字符串的形式打印一个双引号
System.out.println("\"");
// \表示转义字符
// \\前面那个\是一个转移字符,改变了后面那个\原本含义,把它变成一个普普通通的\而已
System.out.println("D:\\JavaProjects\\allProject\\myapi\\src\\demo7\\RegexDemo2.java");
// \\d表示任意的一个数字
//两个\表示一个\,前面的\对后面的\进行转义,把后面的\变成一个普普通通的\
System.out.println("3".matches("\\d"));//true
}
X? | X,一次或0次 |
X* | X,零次或多次 |
X+ | X,一次或多次 |
X{n} | X,正好n次 |
X{n,} | X,至少n次 |
X{n,m} | X,至少n但不超过m次 |
正则表达式可以使用插件"AnyRule"快速生成
正则表达式小结
符号 | 含义 | 举例 |
[ ] | 里面的内容出现一次 | [0-9] [a-zA-Z] |
() | 分组 | a(bc)+ |
^ | 取反 | [^abc] |
&& | 交集,不能写单个的& | [a-z&&m-p] |
| | 写在方括号外表示并集 | [a-zA-z0-9] X|x |
. | 任意字符 | \n回车符号不匹配 |
\ | 转义字符 | \\d |
\\d | 0-9 | \\d+ |
\\D | 非0-9 | \\D+ |
\\s | 空白字符 | [\t\n\x0B\f\r] |
\\S | 非空白字符 | [^\s] |
\\w | 单词字符 | [a-zA-Z_0-9] |
\\W | 非单词字符 | [^\w] |
? | 0次或1次 | \\d? |
* | 0次或多次 | \\d* (abc)* |
+ | 1次或多次 | \\d+ (abc)+ |
{} | 具体次数 | a{7} \\d{7,19} |
(?!) | 忽略后面字符的大小写 | (?!)abc |
a((?!)b)c | 只忽略b的大小写 | a((?!)b)c |
2.爬虫
利用正则表达式在一段文本中查找满足要求的内容
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo1 {
public static void main(String[] args) {
/*有如下文本,按要求爬取
* Java自从问世以来,经历了很多版本,目前企业中使用最多的是Java8和Java11,
* 因为这两个是长期支持版本,下一个长期支持版本是Java17,相信不久将来Java17也会登上历史舞台
* 要求:找出所有的JavaXX*/
String str = "Java自从问世以来,经历了很多版本,目前企业中使用最多的是Java8和Java11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信不久将来Java17也会登上历史舞台";
//Pattern:表示正则表达式
//Matcher:文本匹配器,按照正则表达式的规则去读取字符串,从头开始读取
//获取正则表达式对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//获取文本匹配器对象
//m要在str字符串中找符合p规则的小串
Matcher m = p.matcher(str);
//拿着文本匹配器从头开始读取,寻找是否有满足规则的子串
//如果没有,方法返回false
//如果有,方法返回true,并记录符合要求的子串的索引
while(m.find()){
//方法底层会根据find方法记录的索引进行字符串的截取并返回
String s = m.group();
System.out.println(s);
}
}
}
贪婪爬取和非贪婪爬取
贪婪爬取:在爬取数据的时候尽可能多的获取数据
非贪婪爬取:在爬取数据的时候尽可能少的获取数据
Java中默认是贪婪爬取,如果我们在数量词+、*后面加上问号,那么此时就是非贪婪爬取
只写+或*表示贪婪爬取 +?或*?表示非贪婪爬取
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo2 {
public static void main(String[] args) {
String str = "Java自从问世以来,经历了很多版本,abbbbbbbbbb";
//贪婪爬取
String regex = "ab+";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
while (m.find()){
System.out.println(m.group());//abbbbbbbbbb
}
//非贪婪爬取
String regex1 = "ab+?";
Pattern p1 = Pattern.compile(regex1);
Matcher m1 = p1.matcher(str);
while (m1.find()){
System.out.println(m1.group());//ab
}
}
}
正则表达式在字符串方法中的使用
方法名 | 说明 |
public String[ ] matches(String regex) | 判断字符串是否满足正则表达式的规则 |
public String replaceAll(String regex,String newStr) | 按照正则表达式的规则进行替换 |
public String[ ] split(String regex) | 按照正则表达式的规则切割字符串 |
3.分组
分组就是一个小括号
正则表达式的分组有两种:
捕获分组、非捕获分组
捕获分组:
可以获取每组中内容反复使用
捕获分组规则:
每组的组号就是序号
从1开始,连续不间断
以左括号为基准,最左边的是第一组,其次是第二组,以此类推
public static void main(String[] args) {
//举例:a123a !123! &abc& a123b
// \\1:表示把第1组的内容再拿出来用一次
String regex = "(.).+\\1";
System.out.println("a123a".matches(regex));//true
System.out.println("!123!".matches(regex));//true
System.out.println("&abc&".matches(regex));//true
System.out.println("a123b".matches(regex));//false
System.out.println("------------------------");
//举例:abc123abc c123c 12345123 @!123@! 123ac23
String regex1 = "(.+).+\\1";
System.out.println("abc123abc".matches(regex1));//true
System.out.println("c123c".matches(regex1));//true
System.out.println("12345123".matches(regex1));//true
System.out.println("@!123@!".matches(regex1));//true
System.out.println("123ac23".matches(regex1));//false
System.out.println("--------------------------------");
//举例:aaa123aaa !!!abc!!! 1112351111 @@!ab@@ &&123&!
//(.)把首字母看成一组
// \\2把首字母拿出来再次使用
// *:作用于\\2,表示后面重复的内容出现0次或多次
String regex2 = "((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex2));//true
System.out.println("!!!abc!!!".matches(regex2));//true
System.out.println("1112351111".matches(regex2));//true
System.out.println("@@!ab@@".matches(regex2));//true
System.out.println("&&123&!".matches(regex2));//false
}
后续还要继续使用本组的数据。
正则表达式内部使用:\\组号
正则表达式外部使用:$组号
public static void main(String[] args) {
String s = "我要吃吃吃吃吃吃吃吃好好好好好好吃的";
String str = s.replaceAll("(.)\\1+", "$1");
System.out.println(str);//我要吃好吃的
}
非捕获分组:
分组之后不需要再用本组数据,仅仅是把数据括起来,不占用组号
符号 | 含义 | 举例 |
(?:正则) | 获取所有 | Java(?:8|11|17) |
(?=正则) | 获取前面部分 | Java(?=8|11|17) |
(?!正则) | 获取不是指定内容的前面部分 | Java(?!8|11|17) |