黑马程序员--java基础之正则表达式

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

以前在公司上班的时候,因为工作上的需要,同事写了一个在网页上抓取数据的程序,然后放在公司的服务器,不停的运行,然后从别人的网站上抓取我们需要的数据,每隔3秒就访问一次网页,然后从新的网页上抓取数据。当时感觉很牛B,毕竟叫我弄,我是搞不定的,因为我不懂正则表达式。如今,咱也学习正则表达了,嘿嘿  大笑

咱们先来了解一下正则的知识

正则表达式:符合一定规则的表达式
作用:用于专门操作字符串
特点:用于一些特定的符号来表示一些代码操作。这样就简化书写。所以学习正则表达式,就是在学习一些特殊符号的使用。

好处:可以简化对字符串的复杂操作。
弊端:符号定义越多,正则越长,阅读性越差。

以我们现阶段学习的知识来检验一个QQ号是否合格(不是以0开头,长度为5-15位的数字)
public static void checkQQ()
	{
		String qq="23414243";
		int len=qq.length();
		if(len>=5&&len<=15)
		{
			if(!qq.startsWith("0"))
			{
				char[] arr=qq.toCharArray();
				boolean flag=true;
				for(int x=0;x<arr.length;x++)
				{
					if(!(arr[x]>='0' &&arr[x]<='9'))
					{
						flag=false;
						break;
					}
				}
				if(flag)
				{
					System.out.println("qq:"+qq);
				}
				else
				{
					System.out.println("qq号不符合规则");
				}
			}
		}
	}

从上面可以看出,仅仅是判断一个QQ号,用我们之前学习的知识来完成,要写一大堆的判断语句,非常麻烦。有没有简单的方法呢?duang  duang  duang  主角闪亮登场!!!!答案是有,它就是正则表达式!!!!!瞬间高大上了。。。。
用正则表达式如何做呢?
public static void method()
	{
		//不能以0开头,长度为5-15位的QQ号
		String str="1236465";
		String regex="[1-9]\\d{4,14}";
		boolean flag=str.matches(regex);
		System.out.println(flag);
	}

哇哦,屌爆了有没有!!!只有四句!!!哦,regex这个变量的值不懂?下面就来了。。。

字符
x字符 x
\\反斜线字符
\0n带有八进制值 0 的字符 n (0 <= n <= 7)
\0nn带有八进制值 0 的字符 nn (0 <= n <= 7)
\0mnn带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7)
\xhh带有十六进制值 0x 的字符 hh
\uhhhh带有十六进制值 0x 的字符 hhhh
\t制表符 ('\u0009')
\n新行(换行)符 ('\u000A')
\r回车符 ('\u000D')
\f换页符 ('\u000C')
\a报警 (bell) 符 ('\u0007')
\e转义符 ('\u001B')
\cx对应于 x 的控制符
 
字符类
[abc]abc(简单类)
[^abc]任何字符,除了 abc(否定)
[a-zA-Z]az AZ,两头的字母包括在内(范围)
[a-d[m-p]]ad mp[a-dm-p](并集)
[a-z&&[def]]def(交集)
[a-z&&[^bc]]az,除了 bc[ad-z](减去)
[a-z&&[^m-p]]az,而非 mp[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]
 
POSIX 字符类(仅 US-ASCII)
\p{Lower}小写字母字符:[a-z]
\p{Upper}大写字母字符:[A-Z]
\p{ASCII}所有 ASCII:[\x00-\x7F]
\p{Alpha}字母字符:[\p{Lower}\p{Upper}]
\p{Digit}十进制数字:[0-9]
\p{Alnum}字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct}标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~


再来看[1-9]\\d{4,14}:表示第一位是由1~9等数字组成。从第二位开始都是数字,而且数字的长度是4-14个,加上首位,长度就是5~15个了。。。



正则表达式具体操作功能

1、匹配 : String matches()    用规则匹配整个字符串,只要有一处不符合规则,就匹配结束,返回false 

/*
	匹配手机号,11位
	手机号段只有13xxx  15xxx  18xxx
	*/
	public static void method_1()
	{
		String tel="18707122014";
		String telReg="1[358]\\d{9}";
		System.out.println(tel.matches(telReg));
	}

2、切割    String[]  split(String regex) 

class RegexDemo
{
	public static void main(String[] args)
	{
		
		
		//使用.切割(单独的.代表任意字符,所以要加上转义字符,而在字符串中要使用转义字符要再加上\)
		method_Split("zhangsan.lisi.wangwu.sunba","\\.");
		method_Split("c:\\abc\\a.txt","\\\\");
		
		/*
		为了可以让规则的结果被重用,可以将规则封装成一个组,用()完成。组的出现 
		都有编号,从1开始,想要使用已有的组,可以通过\n (n就是组的编号)的形式来获取
		*/
		//按叠词切(其中的小括号里的表示一组,后面的1表示后面的和第一组内容一致,.代表任意字符)
		method_Split("sdkkaseqqtryyfkaacgh","(.)\\1");
		//按多个叠词切()
		method_Split("sdkkaseqqqqqtryyyyyyyyyfkaacgh","(.)\\1+");
	
		
	}
	
	
	public static void method_Split(String str,String regex)
	{			
		String[] arr=str.split(regex);
		for(String s:arr)
		{
			System.out.println(s);
		}
	}
	
	public static void method_Split1()
	{
		String str="zhangsan    lisi   wangwu zhaoliu";
		//切割多个空格
		String regex=" +";
		String[] arr=str.split(regex);
		for(String s:arr)
		{
			System.out.println(s);
		}
	}
		
}

以前,当我们遇到一个字符串由多个字符串组成,中间用一个空格隔开的时候,我们往往会通过split(" ")将这个字符串切割成一个字符串数组,但是当中间隔开的不是一个空格,而是多个空格时,我们就会抓狂了。。。。但是现在,可以用一个空格加一个"+"来表示多个空格,同样可以完成切割。是不是很方便?
当用于分隔的不是空格,而是小数点的时候,就用小数点分隔,但是,小数点在这里表示的是任意字符,而我们仅仅想把它作为小数点用,所以就用到了转义字符,而在字符串中,表示转义字符要用两个\\来表示。如上例
若是用\\隔开的咋办?那就是\\\\
注:为了可以让规则的结果被重用,可以将规则封装成一个组,用()完成。组的出现 都有编号,从1开始,想要使用已有的组,可以通过\n (n就是组的编号)的形式来获取。
"(.)\\1"  这个代表的意思是,()中的是一个组,代表的是任意字符,而后面的\\1表示使用编号为1的组的结果,并在一起就是两个相同的任意字符,就是叠词,"(.)\\1+",这里多了个"+"表示多个叠词



3、替换  String replaceAll()

class RegexDemo
{
	public static void main(String[] args)
	{		
		
		//将字符串中长度大于等于5的数字替换成#
		method_Replace("asfdf13845218652asdfgkf23345aff","\\d{5,}","#");
		//将字符串中的叠词替换成"&"
		method_Replace("sdkkaseqqqqqtryyyyyyyyyfkaacgh","(.)\\1+","&");
		//将字符串中的叠词替换成一个,如zzz-->z 
		//通过$来获取前一个规则中的第一个组
		method_Replace("sdkkaseqqqqqtryyyyyyyyyfkaacgh","(.)\\1+","$1");
		
		
	}
	
	//替换
	public static void method_Replace(String str,String reg,String newstr)
	{
		str=str.replaceAll(reg,newstr);
		System.out.println(str);
	}
}


正则表达式的第四个功能   
获取:将字符串中符合规则的子串取出

操作步骤:
1、将正则表达式封装成对象
2、让正则对象和要操作的字符相关联
3、关联后,获取正则匹配引擎
4、通过引擎对符合规则的子串进行操作。如取出

上面的步骤中涉及到了两个类  PatternMatcher

Pattern:正则表达式的编译表示形式。
Matcher:通过解释 Pattern 对 character sequence 执行匹配操作的引擎。通过调用模式的 matcher 方法从模式创建匹配器。

具体使用:
import java.util.regex.*;
class RegexDemo2
{
	public static void main(String[] args)
	{
		getDemo();
	}
	public static void getDemo()
	{
		String str="ming tian jiu yao fang jia le ,da jia";
		System.out.println(str);
		
		/*
		其实String类中的matches方法。用的就是Pattern和Matcher对象来完成的。
		只不过被String的方法封装后,用起来较为简单。但是功能却单一。
		*/
		
		//获取字符串中4个长度的单词
		//\b表示单词边际
		String reg="\\b[a-z]{4}\\b";
		
		//将规则封装成对象
		Pattern p=Pattern.compile(reg);
		
		//让正则对象和要作用的字符串相关联。获取匹配器对象
		Matcher m=p.matcher(str);
		
		/*类似于迭代器*/
		//boolean b=m.find(); //将规则作用到字符串,并进行符合规则的子串查找
		//System.out.println(m.group());//用于获取匹配后结果。
		
		while(m.find())
		{
			System.out.println(m.group());
			System.out.println(m.start()+"...."+m.end());
			
		}
	}
}
结果:


来两个练习:
/*
练习


到底用四种功能中的哪一个呢?或者哪几个呢?
思路方式:
1、如果只想知道该字符串是否对错,使用匹配
2、想要将已有的字符串变成另一个字符串,替换
3、想要按照自定的方式将字符串变成多个字符串,切割。获取规则以外的子串
4、想要拿到符合需求的字符串子串,获取。获取符合规则的子串
*/

import java.util.*;
class RegexTest1
{
	public static void main(String[] args)
	{
		Method_Test1();
		Method_Test2();
		checkMail();
		
	}
	
	/*
	需求:对邮件地址进行校验
	*/
	public static void checkMail()
	{
	    String mail="abc123@sina.com.cn";	
		String reg="[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+"; //较为精确的匹配
		reg="\\w+@\\w+(\\.\\w+)+";//相对不太精确的匹配,因为  "1@1.1"也能匹配正确
		sop(mail.matches(reg));
	}
	
	
	/*
	192.168.1.254   102.49.23.013   10.10.10.10  2.2.2.2  8.109.90.30
	将IP地址进行地址段顺序的排序
	
	1、按照每一段需要的最多的0进行补齐,那么每一段就会至少保证有3位。
	2、将每一段只保留3位。这样,所有的IP地址都是每一段3位
	*/
	public static void Method_Test2()
	{
		String ip="192.168.1.254   102.49.23.013   10.10.10.10  2.2.2.2  8.109.90.30";
		
		ip=ip.replaceAll("(\\d+)","00$1"); //补齐,使每一段至少3位
		sop(ip);
		
		ip=ip.replaceAll("0*(\\d{3})","$1"); //去掉多余的0,使每一段为3位
		sop(ip);
		
		String[] arr=ip.split(" +");  //将字符串转换为数据
		
		TreeSet<String> ts=new TreeSet<String>();
		for(String s:arr) //将数组中的元素添加到TreeSet集合中排序
		{
			ts.add(s);
		}
		
		for(String s:ts)
		{
			sop(s.replaceAll("0*(\\d)","$1"));  //将每一段中多余的0去掉,如002-->2
		}
		
	}
	
	/*
	将下列字符串转成:我要学编程
	*/
	public static void Method_Test1()
	{
		String str="我我...我我...我想要...要要...要要...学学学...学学..编编编..编程..程.程程...程...程";
		/*
		将已有字符串变成另一个字符串,使用替换功能
		1、可以将.去掉
		2、再将多个重复的内容变成单个内容
		*/
		String reg1="\\.+";
		str=str.replaceAll(reg1,"");
		sop(str);
		String reg2="(.)\\1+";
		str=str.replaceAll(reg2,"$1");
		sop(str);
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

应用:网页爬虫
旁白:妈呀,终于到正餐了,这个例子就是我开头说的,在网页上抓取数据的应用,来,咱也搞起。。。。

/*
网页爬虫
*/

import java.io.*;
import java.util.regex.*;
import java.net.*;
class RegexTest2
{
	public static void main(String[] args) throws Exception
	{
		getMail() ;
	}
	
	public static void getMail() throws Exception
	{
		//数据源,从哪里抓取数据
		URL url=new URL("http://tieba.baidu.com/p/2986729518");  //从贴吧里一大堆求种的贴子里选了一个
		URLConnection urlconn=url.openConnection();
		//获取读取流
		BufferedReader brIn=new BufferedReader(new InputStreamReader(urlconn.getInputStream()));
		//将抓取的数据存入一个文件中
		BufferedWriter bwOut=new BufferedWriter(new FileWriter("mailList.txt"));
		
		String line=null;
		String mailReg="[a-zA-Z0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
		//将正则表达式封装成对象
		Pattern p=Pattern.compile(mailReg);
		
		while((line=brIn.readLine())!=null)
		{
			//文本和引擎相关联
			Matcher m=p.matcher(line);
			while(m.find())
			{
				String str=m.group();
				bwOut.write(str);
				bwOut.newLine();
				bwOut.flush();
				System.out.println(str);
			}
		}
	}
}

结果:      怎么样?   大笑

其实还有是有点瑕疵的,上面的截图只能截取部分,看一下文件吧

看到圈红线部分没,这部分肯定是有问题的,毕竟人家发的是QQ邮箱,但是最后截取的去含有字母。。。。。
我们可以在获取的结果里作下判断,如果结果字符串含有@qq,则从数字部分开始。。。但是也有问题,像uff0c开头的还好,后面的就是QQ号了,但是u5b50这样开始的就不好弄了。。。咋办?

看了下网页源代码:  实际上邮箱应该是选中部分,但是结果却是把前面的u300也截取过来了。。。各位看官,有什么方法没?(待续。。。)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值