正则表达式练习 (Java)
本地爬虫
Pattern
:表示正则表达式
Matcher
:文本匹配器,按照正则表达式的规则去读字符串,从头开始读取
-
获取正则表达式的对象
Pattern p = Pattern.compile("Java\\d{0,2}");
-
获取文本匹配器对象
Matcher m = p.matcher(str);//str表示大串
m:文本匹配器对象
str:大串
p:规则
m要在str中找符合p规则的小串
-
拿着文本匹配器从头开始读取,寻找是否有满足规则的子串
boolean b = m.find();
拿着文本匹配器从头开始读取,寻找是否有满足的子串
如果没有找到满足规则的子串,就会返回
false
如果找到了满足规则的子串,就会返回
true
,在底层记录子串的起始索引和结束索引+1//底层源码 public boolean find() { int nextSearchIndex = last; if (nextSearchIndex == first) nextSearchIndex++; // If next search starts before region, start it at region if (nextSearchIndex < from) nextSearchIndex = from; // If next search starts beyond region then it fails if (nextSearchIndex > to) { for (int i = 0; i < groups.length; i++) groups[i] = -1; return false; } return search(nextSearchIndex); }
-
从大串中截取小串
String s1 = m.group();
group
方法底层会根据find方法记录的索引进行字符串的截取,截取完会把截取的小串进行返回在截取字符串的时候会用到subString
subString(起始索引,结束索引);包头不包尾,所以前面是结束索引+1
//底层源码 public String group(int group) { if (first < 0) throw new IllegalStateException("No match found"); if (group < 0 || group > groupCount()) throw new IndexOutOfBoundsException("No group " + group); if ((groups[group*2] == -1) || (groups[group*2+1] == -1)) return null; return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString(); }
-
输出获取符合规则的子串
System.out.println(s1);
全部代码如下
String str ="Java自95年问世以来,经历了很多的版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持的版本,下一个长期支持的版本是Java17,相信在不久的将来Java17也会登上历史的舞台,我觉得Java很棒";
//获取正则表达式的对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//获取文本匹配器对象
Matcher m = p.matcher(str);
//拿着文本匹配器从头开始读取,寻找是否有满足规则的子串
boolean b = m.find();
//方法底层会根据find方法记录的索引进行字符串的截取,截取完会把截取的小串进行返回
String s1 = m.group();
System.out.println(s1);
按照这个步骤只能获取一次的符合要求的子串,要想将整个大串中的所有符合要求的小串爬取出来,就要用for循环,将大串全部读取完成,然后获取出全部符合条件的小串
代码如下
String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
//method1(str);
//获取正则表达式的对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//获取文本匹配器的对象
//拿着m去读取str,找到符合p规则的子串
Matcher m = p.matcher(str);
//用循环操作,当m.find返回值是false是循环结束
while(m.find()){
String s = m.group();
System.out.println(s);
}
网络爬虫
网络爬虫就是在本地爬虫中加入了一些网络连接的内容和文本读取的内容
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Teat {
public static void main(String[] args) throws IOException {
//创建一个URL对象
URL ur1 = new URL("http://www.jn001.com/paperpc/content/content_91055.html");
//连接上这个网址
URLConnection conn = ur1.openConnection();
//创建一个对象去读取网络中的数据
BufferedReader br = new BufferedReader(new BufferedReader(new InputStreamReader(conn.getInputStream())));
String line;
//获取正则表达式的对象
String regex = "[1-9]\\d{17}";
Pattern pattern = Pattern.compile(regex);
while ((line = br.readLine()) != null) {
//拿着文本匹配器的对象matcher按照pattern的规则去获取当前的这一行信息
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
System.out.println(matcher.group());
}
}
br.close();
}
}
带有选择性的数据爬取
要求
有如下文本,按要求爬取数据。
Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是JAva8和JAVA11,因为这两个是长期支持版本,下一个长期支持版本是JavA17,相信在未来不久java17也会逐渐登上历史舞台。
需求1:爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。
需求2:爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17
需求3:爬取除了版本号为8,11,17的Java文本。
创建大串
String s="Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是JAva8和JAVA11,因为这两个是长期支持版本,下一个长期支持版本是JavA17,相信在未来不久java17也会逐渐登上历史舞台。";
要求1
//确定正则表达式
String regex1="((?i)Java)(?=8|11|17)";
//创建正则表达式对象
Pattern pattern1 = Pattern.compile(regex1);
//创建文本选择器对象
Matcher matcher = pattern1.matcher(s);
//循环读取
while (matcher.find()){
System.out.println(matcher.group());
}
?表示前面的部分,例如regex1
里面的((?i)Java)
=表示Java后面的要跟取得数据,但是截取的时候不截取,只截取前半部分
说明:?表示前半部分的规则,在这里就表示
((?i)Java)
这一部分=表示后面要跟的数据,在这里是在要截取相关版本的Java但是不显示相关版本号
要求2
//方法一
String regex2="((?i)Java)(8|11|17)";
Pattern pattern2 = Pattern.compile(regex2);
Matcher matcher = pattern2.matcher(s);
while (matcher.find()){
System.out.println(matcher.group());
}
//方法二
String regex2_1="((?i)Java)(?:8|11|17)";
Pattern pattern2_1 = Pattern.compile(regex2_1);
Matcher matcher = pattern2_1.matcher(s);
while (matcher.find()){
System.out.println(matcher.group());
}
:表示Java后面要跟取得数据,截取的时候截取全部,包括后面的一部分
说明::表示后面截取的数据,表示要根据后面的数据截取,并且要截取全部的,包含前面的java和后面的版本号
要求3
String regex3="((?i)Java)(?!8|11|17)";
Pattern pattern13 = Pattern.compile(regex3);
Matcher matcher = pattern13.matcher(s);
while (matcher.find()){
System.out.println(matcher.group());
}
!表示Java后面不要跟取得数据,截取的时候不截取,只截取前半部分
说明:!表示不要跟符合条件的数据,意思是符合后面这些条件的java都不截取,只截取不符合题的java,跟=是相反的
贪婪爬取和非贪婪爬取
贪婪爬取:在爬取数据的时候尽可能多获取数据,在Java中默认的就是贪婪爬取
非贪婪爬取:在爬取数据的时候尽可能少获取数据,如果在数量词+ *的后面加上问号,那么就是非贪婪爬取
对以下字符串进行贪婪爬取和非贪婪爬取
String s = "Java自从95年问世以来,abbbbbbbbbbbbaaaaaaaaaaaaaaaaaa" +
"经历了很多版木,目前企业中用的最多的是]ava8和]ava11,因为这两个是长期支持版木。" +
"下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
贪婪爬取:按照ab+的方式爬取ab,b尽可能多获取
String regex="ab+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group());
}
非贪婪爬取:按照ab+的方式爬取ab,b尽可能少爬取
String regex="ab+?";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(s);
while (matcher.find()){
System.out.println(matcher.group());
}
正则表达式在字符串中的使用
方法名 | 说明 |
---|---|
public String[] marches(String regex) | 判断字符串是否满足正则表达式的规则 |
public String replaceAll(String regex,String newStr) | 按照正则表达式的规则进行替换 |
public String[] split(String regex) | 按照正则表达式的规则进行切割字符串 |
有下面一段字符串,用正则表达式完成要求
小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠
要求1:把字符串中三个姓名之间的字母替换为vs
要求2:把字符串中的三个姓名切割出来
创建大串
String s="小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
要求一:把字符串中三个姓名之间的字母替换为vs
String replaceAll = s.replaceAll("[\\w&&[^_]]+", "vs");
System.out.println(replaceAll);
细节:
方法在底层跟之前一样创建文本解析器的对象
然后从头开始去读取字符串中的内容,主要满足的,俺么就用第二个参数替换
//String.replaceAll源码
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
要求二:把字符串中的三个姓名切割出来
String[] split = s.split("[\\w&&[^_]]+");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
说明
split是将符合正则表达式的内容给切割掉,然后将切割完剩下的部分放到String[]中
分组括号
每组是有括号的,也就是序号
规则一:从1开始,连续不间断
规则二:以左括号为基准,最左边的石堤一组,其次是第二组,以此类推
捕获分组
捕获分组就是把这一组的数据捕获出来,再用一次
需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
举例: a123a b456b 17891 &abc& a123b(false)
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));
需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
举例: aaa123aaa bbb456bbb 111789111 &&abc&&
String regex3 = "((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex3));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("aaa123aab".matches(regex3));
说明:
(.)
把首字母看做一组
\\2
把首字母拿出来再次使用。
*
作用于\\2
,表示后面重复的内容出现零次或者多次
后续还要继续使用本组的数据
正则内部使用:\\组号
正则外部使用:$组号
需求
将字符串:我要学学编编编编程程程程程程
替换为:我要学编程
String str = "我要学学编编编编程程程程程程";
String result = str.replaceAll("(.)\\1+", "$1");
System.out.println(result);
(.)
表示把重复内容的第一个字符看做一组
\\1
表示第一字符再次出现+
至少一次
$1` 表示把正则表达式中第一组的内容,再拿出来用
费捕获分组
分组之后不需要再用本组数据,仅仅是把数据活起来,不占用组号
符号 | 含义 |
---|---|
(?:正则) | 获取所有 |
(?=正则) | 获取前面的部分 |
(?!正则) | 获取不是指定内容的前面部分 |
(.)
表示把重复内容的第一个字符看做一组
\\1
表示第一字符再次出现+
至少一次
$1` 表示把正则表达式中第一组的内容,再拿出来用
费捕获分组
分组之后不需要再用本组数据,仅仅是把数据活起来,不占用组号
符号 | 含义 |
---|---|
(?:正则) | 获取所有 |
(?=正则) | 获取前面的部分 |
(?!正则) | 获取不是指定内容的前面部分 |