黑马毕向东Java课程笔记(35天day28第二部分28-1——28-10)正则表达式

1、正则表达式概述
  正则表达式用于操作字符串数据,通过一些特定的符号来体现的,所以我们为了掌握正则表达式,必须要学习一些符号。正则表达式虽然简化了某一些内容,但是代码的阅读性差。 相应的示例如下:

package regex.demo;

public class RegexDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String qq = "1235456";
//		checkQQ(qq);

		//上面的判断太麻烦了,写了很多代码,如果使用正则表达式就很简单。
//String类的一个方法:boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式。 
//下面的正则表达式[1-9]表示第一位取1-9之间,[0-9]表示第二位取0-9之间,而{4,14}表示第一位后面跟的字符只能是4-14个,且规则是[0-9]
		String regex = "[1-9][0-9]{4,14}";
		boolean b = qq.matches(regex);//开始匹配		
		System.out.println(qq+":"+b);
		
		//比如,我们想判断输入的字符串头尾是a,b中间只能跟4-6个o
		String str = "aoooooooob";
		String regex_1 = "ao{4,6}b";
		boolean b1 = str.matches(regex_1);
		System.out.println(str+":"+b1);
	}

	//需求:定义一个功能对QQ号进行校验,传入的qq号字符串长度5~15. 只能是数字, 0不能开头
	public static void checkQQ(String qq)
	{
		
		if(qq.length()>=5 && qq.length()<=15)
		{
			if(!qq.startsWith("0"))
			{
//当qq字符串长度正确,格式正确之后,我们需要检测其是否为数字,直接使用Long类的parseLong()方法转换
//如果qq含有数字之外的非法字符,我们就会捕捉到异常并抛出。因为字符串转换得到的数可能十几位,特别大,那么必须用Long来表述
//普通方法是拆分字符串为数组判断每一个数是不是数字,太麻烦!(注意此处使用的判断字符串是否都是数字的方法!)
				try
				{
					long l = Long.parseLong(qq);
					System.out.println(l+":正确");
				}
				catch(NumberFormatException e)
				{
					System.out.println(qq+"含有非法字符");
				}
			}
			else
			{
				System.out.println(qq+"格式不正确");
			}
		}
		else
		{
			System.out.println(qq+"长度不正确");
		}
	}
}

2、正则表达式常见规则
  正则表达式常见的操作如下(具体见API文档中关于正则表达式的介绍),有一些地方需要专门记忆!

/*
正则表达式对字符串的常见操作:
1、匹配。其实使用的就是String类中的matches方法。

2、切割。其实使用的就是String类中的split方法。 

3、替换。其实使用的就是String类中的replaceAll()方法。

 4,获取。

  相应的代码示例如下:
  关于正则表达式中反斜杠的使用,参考文章:添加链接描述

package regex.demo;

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

public class RegexDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		functionDemo_4();
	}

	//演示匹配。 
	public static void functionDemo_1()
	{
		//判断手机号是否匹配
		String number = "13826567973";
//这个正则表达式:第一位为1,第二位必须是3、5、8其中一个,\d表示0-9数字,
//因为正则表达式中\d表示数字,而在java的字符串中,如果写“\d”,反斜杠“\”会自动将“d”转义,既“\d”在字符串中表示d被反斜杠转义(我们不知道d转义后表示什么,这没所谓),所以就必须再加一个反斜杠,将前面的“\”转义为普通的“\”,既字符串中“\\”表示一个普通的“\”,而不是有转义功能的“\”,普通反斜杠和d组合,就成为正则表达式。因此字符串中“\\d”表示正则表达式“\d”(视频3-5.25秒开始的解析)
		String regex = "1[358]\\d{9}";//也可以写作:"1[358][0-9]{9}"
		boolean result = number.matches(regex);
		System.out.println(number+":"+result);
	}
	
	// 切割。
	public static void functionDemo_2()
	{
		String str = "zhangsan   lisi     wangwu";
		String[] names = str.split(" +");//"+"表示一次或者多次
		for(String name:names)
		{
			System.out.println(name);
		}
		
		String str1 = "zhangsan.lisi.wangwu";
//用“.”切割,但是“.”是正则表达式的特殊符号,需要加“\”来取消“.”的正则表达式涵义,而“\.”是转义字符,在字符串中,“\.”任然是有正则表达式功能的“.”,再加一个“\\”表示普通的“\”,用于取消“\”的转义字符功能,“\\”表示一个普通的“\”,在字符串中普通的反斜杠可以取消“.”的正则表达式功能,那么在字符串中,“\\.”就是一个普通的“.”
//而对于“\d”,它有正则表达式的涵义,我们在字符串中使用的时候,“\d”会被表示为转义字符,因此我们加一个“\”,变为“\\”,这样就可以把“\”的转义功能取消(视频4,5.30解析),字符串中,“\\d”就是一个有正则表达式功能的符号。
(注意,普通的“\”和相应的字符一组才能表示正则表达式,而字符串中的问题是普通“\”在字符串中有转义的功能,因此需要加多一个“\”将后面的“\”变为普通的“\”,既字符串中“\\”表示一个普通的“\”)
//根据 Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "\b" 与单个退格字符匹配,而 "\\b" 与单词边界匹配。字符串字面值 "\(hello\)" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 "\\(hello\\)"。
		String[] names1 = str1.split("\\.+");
		for(String name:names1)
		{
			System.out.println(name);
		}
		
		String str2 = "zhangsan66666lisiHHHwangwu";//将姓名切割出来,切割的分界是“*****”一排数量不确定的相同字符
//正则表达式:组的概念(35-28(part2)-04-9.35),正则表达式用“()”来封装表达式,类似于函数的封装,
//封装完成之后会自动编号(从1开始),这个编号就是封装的名字。
//如"(.)\\1+",第一位可以是任意字符,用“.”表示并封装编号为1,接着第二位与第一位相同,直接调用编号1就可以,那么用“\\1”表示第一组,此时1为组标号
//+表示从第二位开始用1或多个位,并且这些位的内容与第一组相同
		String[] names2 = str2.split("(.)\\1+");
		for(String name:names2)
		{
			System.out.println(name);
		}
		//关于组的解析:(35-28(part2)-04-13.20)
/*
如果组比较复杂((A)(B(C)))——从左括号“(”开始看,左括号有几个就有几个组
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组: 
1     ((A)(B(C))) 
2     \A 
3     (B(C)) 
4     (C) 

*/
	}
	
	//替换
	public static void functionDemo_3() 
	{
//说明:对于replaceAll(regex,replacement),regex是用来匹配此字符串的正则表达式
//regex可以只匹配我们想替换的部分,也可以匹配整个字符串。但是后面的replacement会将regex全部替换。
		
		String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";
		//首先,将叠词替换为其他字符串
//		str = str.replaceAll("(.)\\1+", "@");//zhangsan@xiaoqiang@zhaoliu
//		System.out.println(str);
		
		//如果我们想把“tttt”替换为一个“t”,把“mmmmmm”替换为一个“m”,如何操作?
		//既第二个参数想使用第一个参数的内容。可以用“$”获取前一个参数的正则表达式内容,“$1”表示获取第一个参数的第一组
		str = str.replaceAll("(.)\\1+", "$1");//(regex代表想替换的部分)
		System.out.println(str);//zhangsantxiaoqiangmzhaoliu
		
		//练习一下(regex代表整个字符串)
		String tel = "15800001111";//替换成为158****1111;
//"(\\d{3})\\d{4}(\\d{4})", "$1****$2",(\\d{3})表示字符串前3个字符是数字,并表示第一组;\\d{4}表示4-7位是数字;
//(\\d{4})表示8-11位是数字,并标为第二组。而替换的"$1****$2"会将regex全部替换,其中“$1”为regex中的第一组,后面是4个“****”,
//再后面跟着“$2”为regex中的第二组
		tel = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
		System.out.println(tel);
	}
	
	//获取
	//说明一下,正则表达式其实也是一个类 Pattern:正则表达式的编译表示形式。 
/*(35-28(part2)-06-2.00)
指定为字符串的正则表达式必须首先被编译为此类的实例(把正则表达式封装为对象)。
然后,可将得到的模式(模式就是正则规则)用于创建 Matcher 对象(Matcher是匹配器,用于保存匹配结果),依照正则表达式,该对象可以与任意字符序列匹配。
执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。 

因此,典型的调用顺序是 

//static Pattern compile(String regex) 将给定的正则表达式编译到模式中。 
 Pattern p = Pattern.compile("a*b");//将正则规则进行对象的封装。
 
 //Matcher matcher(CharSequence input) 创建匹配给定输入与此模式的匹配器。 
 //通过正则对象的matcher方法字符串相关联。获取要对字符串操作的匹配器对象Matcher
 Matcher m = p.matcher("aaaaab");
 
 boolean b = m.matches();//通过Matcher匹配器对象的方法对字符串进行操作。
 */
	public  static void functionDemo_4() 
	{
		String str = "da jia hao,ming tian bu fang jia!";//获取其中包含三个字母的字符串
		//首先定义正则规则——\\b是单词边界,[a-z] {3}表示a-z任意取3个,最后还要再加一个单词边界\\b
		String regex = "\\b[a-z]{3}\\b";
		Pattern p = Pattern.compile(regex);//将正则规则封装为Pattern对象
		Matcher m = p.matcher(str);//获取相应的匹配器对象,并将想操作的字符作为matcher()方法的参数
		
		//false:str与所描述的规则不匹配,当然了,这是因为我们的规则是想用来查找str内容,不是为了匹配str,想匹配直接使用String的matches方法即可
		System.out.println(m.matches());
		
		//使用Matcher对象的方法对字符串进行操作,既然要获取三个字母组成的单词 ,使用Matcher的查找方法: find();
		System.out.println(str);
//boolean find() 尝试查找与该模式匹配的输入序列的下一个子序列。
//String group() 返回由以前匹配操作所匹配的输入子序列。 
//int start() 返回以前匹配的初始索引。 
//int end() 返回最后匹配字符之后的偏移量。 
		while(m.find())
		{
			System.out.println(m.group());//获取匹配的子序列
			System.out.println(m.start()+":"+m.end());		
		}
/*
da jia hao,ming tian bu fang jia!
jia
3:6
hao
7:10
jia
29:32

*/
	}
}

3、正则表达式练习
  3个练习的要求与相关代码如下:

1、治疗口吃:我我...我我...我我我要...要要要要...要要要要..学学学学学...学学编编...编编编编..编..程程...程程...程程程
2、对ip地址排序。 
3、对邮件地址校验。 
package regex.demo;

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

public class RegexDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		test_3();
	}
	
	//练习1:治疗口吃
	public static void test_1()
	{
		String str = "我我...我我...我我我要...要要要要...要要要要..学学学学学...学学编编...编编编编..编..程程...程程...程程程";
		//首先,将“.”用空字符替换“”表示空白字符
		str = str.replaceAll("\\.+", "");//"\\.+"表示一个或者多个空白“.”,""表示空白字符
		System.out.println(str);//我我我我我我我要要要要要要要要要学学学学学学学编编编编编编编程程程程程程程
		
		//接着,将叠词替换为单个词
		str = str.replaceAll("(.)\\1+", "$1");
		System.out.println(str);//我要学编程
	}
	
	/*
	 * ip地址排序。  
	 * 192.168.10.34 127.0.0.1 3.3.3.3  105.70.11.55
	 * 
	 * 说到排序,我们必须先获取每一段的ip地址,然后判断每一个ip地址开头的数字,这样显然很麻烦
	 * 我们可以将ip地址切割出来之后,将其存放到集合中,而有的集合有默认的排序的特性,这样我们便不需要手动编写排序的代码
	 * ip地址是不可重复的,考虑Set集合,又要有顺序,那么就算TreeSet,而String有默认实现Comparable接口,有自然排序特性,可以直接排序
	 * 我们在比较的时候希望每一段的ip地址是一样长的,这样TreeSet才能识别,那么可以在每一段前面补0,排序后再将0取出
	 */
	public static void test_2()
	{	
		String ip_str = "192.168.10.34  127.0.0.1  3.3.3.3  105.70.11.55";
		
//首先,我们知道有的段只有1,而有的段是3,我们先将所有的段开头补2个0,再将多余的0取出,获得每一段长度都是3的ip地址
//注意此处这种先添加后删除从而获得标准长度的方法
		
//		//(\\d+)表示将每一段数字封装为一个组,"00$1"表示将第一组(实际上也只有一个组)前面加2个0
		ip_str = ip_str.replaceAll("(\\d+)", "00$1");
		System.out.println("第一次:"+ip_str);
		//"0*(\\d{3})"表示最后有3个数字,而前面有0到多个0的字段,并将后面的3个数字端封装为组,传递给replacement
		ip_str = ip_str.replaceAll("0*(\\d{3})", "$1");
		System.out.println("第二次:"+ip_str);
		
		//接下来,将ip地址取出,并放到TreeSet集合里面
		TreeSet<String> ts = new TreeSet<String>();
		//先切割
		String[] ip_array = ip_str.split(" +");
		for(String ip:ip_array)
		{
			ts.add(ip);
//将ip地址添加到ts集合中,这里ip地址变为192.168.010.034  127.000.000.001  003.003.003.003  105.070.011.055
//这样TreeSet集合便有办法比较,之前192.168.10.34  127.0.0.1  3.3.3.3  105.70.11.55没办法比较
		}
		//将TreeSet集合的ip地址取出并去除多余的0
		for(String ip:ts)
		{
			System.out.println(ip.replaceAll("0*(\\d+)", "$1"));
		}
/*结果:正确排序
3.3.3.3
105.70.11.55
127.0.0.1
192.168.10.34

如果我们不先添加0再切割比较,结果是
105.70.11.55
127.0.0.1
192.168.10.34
3.3.3.3
因为TreeSet的自然顺序会先比较第一个数字,这样3.3.3.3就变成最大的ip地址了,因为3是里面最大的数
*/
	}
	
	//对邮件地址校验。
	public static void test_3() 
	{
		String mail = "42re_sdg90@qq.com.cn";
		String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{2,3})+";
		//\w 单词字符:[a-zA-Z_0-9] 根据这个式子,regex也可以笼统写作
		String regex1 = "\\w+@\\w+(\\.[a-zA-Z] {2,3})+";
		
		boolean b = mail.matches(regex);	
		System.out.println(mail+":"+b);
	}
}

4、正则表达式爬虫练习
  相关代码如下:

package regex.demo;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexDemo {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception 
	{
		List<String> list = getMail();	
		for(String mail : list){
			System.out.println(mail);
		}
	}

	//首先,从本地一个文件读取邮箱名字符串,符合规则添加到集合中存储。
	//此处获取的邮箱可以重复,而且需要排序,使用List集合的ArrayList
	public static List<String> getMail() throws Exception
	{
//		首先从本地文件获取邮箱名称,因为都是字符串,使用字符流读取,并使用字符串缓冲区
		BufferedReader bufr = new BufferedReader(new FileReader("G:\\mail.html"));
		//再创建一个ArrayList集合用于存储字符串
		List<String> al = new ArrayList<String>();
		
		//首先,创建邮箱名的正则表达规则
		String regex = "\\w+@\\w+(\\.\\w{1,3})+";
		//接着,根据regex创建Pattern对象
		Pattern p = Pattern.compile(regex);
		
		//接着,读取每一行的内容,每一行都是一个邮箱名,读取一行就将这一行的字符串赋予p的matcher方法,获取相应的匹配器对象
		String line = null;
		while((line=bufr.readLine()) != null)
		{
			//将邮箱名赋予p的matcher方法,用于获取相应的匹配器对象
			Matcher m = p.matcher(line);
			//如果寻找到满足条件的邮箱,将其存储到集合中
			while(m.find())//因为每一行出来邮箱之外可能还有其他的字符串数组,也可能有多个邮箱,那么就在这一行不断查找
			{
				al.add(m.group());
			}
		}
		return al;//返回存储相应邮箱名的集合
	}
	
	//其次,从网络上一个界面读取邮箱名字符串,符合规则添加到集合中存储。
	public static List<String> getMailByWeb() throws Exception
	{
		//首先,根据一个网址创建一个URL对象。
//此处是我们向IP地址为192.168.1.195的地址的8080端口服务器发送获取/myweb/mail.html文件的请求(本机没有Tomcat无法演示)
		URL url = new URL("http://192.168.1.195:8080/myweb/mail.html");
		InputStream ins = url.openStream();//获取url的读取流
		BufferedReader bufr = new BufferedReader(new InputStreamReader(ins));//将读取字节流转换为字符串读取缓冲区
		
		List<String> al = new ArrayList<String>();
		String regex = "\\w@\\w(\\.\\w{1,3})+";
		Pattern p = Pattern.compile(regex);
			
		String line = null;
		while((line=bufr.readLine()) != null)
		{
			Matcher m = p.matcher(line);
			while(m.find())
			{
				al.add(m.group());
			}
		}
		return al;//返回存储相应邮箱名的集合
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值