黑马程序员-正则表达式

-----------android培训java培训、java学习型技术博客、期待与您交流! ------------


一、正则表达式概述


1、定义

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,
组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

2、给定一个正则表达式和另一个字符串,可以达到如下目的:

(1)给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”);
(2)可以通过正则表达式,从字符串中获取我们想要的特定部分。

3、正则表达式的特点是:

(1)灵活性、逻辑性和功能性非常的强;
(2)可以迅速地用极简单的方式达到字符串的复杂控制。
(3)对于刚接触的人来说,比较晦涩难懂。

4、应用:

由于正则表达式主要应用对象是文本,因此它在各种文本编辑器场合都有应用,
小到著名编辑器EditPlus,大到Microsoft Word、Visual Studio等大型编辑器,
都可以使用正则表达式来处理文本内容。

二、常用符号:


说明:X表示字符X或者匹配的规则。

1、字符


构造                                    匹配

\\                                     反斜线字符

\t                                     制表符

\n                                    回车符

\f                                     换页符

2、字符类


[abc]                              a、b或c(简单类)

[^abc]                            任何字符,除了a、b或c(否定)

[a-zA-Z]                         a到z或A到Z

[a-d[m-p]]                     a到d或m-p:[a-dm-p](并集)

3、预定义字符类


.                                     任何字符(与行结束符可能匹配也可能不匹配)

\d                                   数字: [0-9]

\D                                  非数字:  |^[0-9]

\s                                   空白字符:[ \t\n\x0B\f\r]

\S                                  非空白字符:[^ \t\n\x0B\f\r]

\w                                 单词字符:[a-zA-Z_0-9]

\W                                 非单词字符:[^\w]

4、边界匹配器


^                                  行的开头

$                                  行的结尾

\b                                 单词边界

\B                                 非单词边界

\A                                 输入的开头

5、Greedy数词量---->匹配整个字符串


X?                                 X,一次或一次也没有

X*                                 X,零次或多次

X+                                X,一次或多次

X{n}                              X,恰好n次

X{n,}                             X,至少n次

X{n,m}                          X,至少n次,但不超过m次

6、组和捕获


捕获组可通过从左到右计算其开括号来编号。例如,在表达式((A)(B(C))) 中,存在四个这样的组:

1     ((A)(B(C))) 
2     \A 
3     (B(C)) 
4     (C) 

组零始终代表整个表达式 。


三、正则表达式具体功能


主要有四种具体功能:匹配、切割、替换和获取

1、匹配


public boolean matches()
尝试将整个区域与模式匹配。 

如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。 
只要有一处不符合规则,就匹配结束,返回false
当且仅当整个区域序列匹配此匹配器的模式时才返回 true。

代码示例1:匹配QQ号码


QQ限定在5-11位之间
第1位不能是0,必须是1-9
第2位以及以后都是数字


class MatchesQQ{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args) {
		String qq1 = "012345";
		String qq2 = "a12345";
		String qq3 = "-123456";
		String qq4 = "1234";
		String qq5 = "123456789012";
		String qq6 = "123456789";
		String reg = "[1-9]\\d{4,10}";
		sop("qq1:"+qq1.matches(reg));//false
		sop("qq2:"+qq2.matches(reg));//false
		sop("qq3:"+qq3.matches(reg));//false
		sop("qq4:"+qq4.matches(reg));//false
		sop("qq5:"+qq5.matches(reg));//false
		sop("qq6:"+qq6.matches(reg));//true
	}
}

代码示例2:匹配手机号码


匹配前两位,只要后面9位都是数字就行,
当然目前有的号段并未开通,但暂时不考虑。
手机号码有13XXX、15XXX、18XXX号段

class MatchesTel{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args) {
		String tel1 = "12011110000";
		String tel2 = "186111100002";
		String tel3 = "1351111000";
		String tel4 = "A3511112222";
		String tel5 = "18600001111";
		//手机号码必须是11位数字
		//第1位不能是0,必须是1
		//第2位只能是3、5、8.
		//第3位到第11位都是任何数字
		String reg = "[1][358]\\d{9}";
		sop("tel1:"+tel1.matches(reg));//false
		sop("tel2:"+tel2.matches(reg));//false
		sop("tel3:"+tel3.matches(reg));//false
		sop("tel4:"+tel4.matches(reg));//false
		sop("tel5:"+tel5.matches(reg));//true
	}
}

代码示例3:匹配用户名


用户名长度在6-15位之间
用户名以字母和数字组成,只能以字母开头,只能包含'-'以及'_'
^ 行的开头 只能是任何字母大小写
$ 行的结尾

class MatchesUser{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args) {
		String user1 = "wgxin";
		String user2 = "jiewin";
		String user3 = "wgx*abc";
		String user4 = "020jiuguijiu";
		String user5 = "A6_1-2345612345";
		String reg = "^[a-zA-Z][a-zA-Z0-9_-]{5,14}$";
		sop("user1:"+user1.matches(reg));//false
		sop("user2:"+user2.matches(reg));//true
		sop("user3:"+user3.matches(reg));//false
		sop("user4:"+user4.matches(reg));//false
		sop("user5:"+user5.matches(reg));//true
	}
}

2、切割


用到的方法:split(String regex

代码示例:对字符串进行切割。


class SplitDemo{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args) {
		//splitUser1();
		//splitUser2();
		//splitUser3();
		//splitURL();
		splitDieCi();
	}
	//如果切割符号只出现连续1个
	public static void splitUser1(){
		String user = "zhangsan lisi wangwu";
		String reg = " ";//用一个空格切割
		String[] arr = user.split(reg);
		for(String s:arr){
			sop(s);
		}
	}
	//切割符号连续出现多次并且个数不相等
	public static void splitUser2(){
		String user = "zhangsan  lisi    wangwu";
		String reg = " +";//用多个空格切割
		String[] arr = user.split(reg);
		for(String s:arr){
			sop(s);
		}
	}
	//切割符号不能用“.”切割,因为它是正则表达式里的特殊符号,必须先进行转义
	public static void splitUser3(){
		String user = "zhangsan.lisi.wangwu";
		String reg = "\\.";//不能这样写\.
		String[] arr = user.split(reg);
		for(String s:arr){
			sop(s);
		}
	}
	//使用叠词切割
	public static void splitDieCi(){
//		//用.来表示任意字符,用(.)1来表示一组。
//		//但是1必须先用\转义,同时\也要转义一次,最后就是(.)\\1
//		String user = "zhangshankklisibbwangwu";
//		String reg = "(.)\\1";

		//如果叠词数出现的个数不同,就在后面添加‘+’
		String user = "zhangshankklisibbwangwu";
		String reg = "(.)\\1";
		reg =  "(.)\\1+";
		String[] arr = user.split(reg);
		for(String s:arr){
			sop(s);
		}
	}
}

3、替换


replaceAll
public String replaceAll(String replacement)
替换模式与给定替换字符串相匹配的输入序列的每个子序列。 

此方法首先重置匹配器。然后,它将扫描输入序列以查找该模式的匹配项。
不属于任何匹配的字符被直接添加到结果字符串;在结果中每个匹配都将被替换字符串所替换。
替换字符串可能包含到已捕获子序列的引用,如在 appendReplacement 方法中一样。 

注意,在替换字符串中使用反斜线 (\) 和美元符号 ($) 可能导致与作为字面值替换字符串时所产生的结果不同。
美元符号可视为到如上所述已捕获子序列的引用,反斜线可用于转义替换字符串中的字面值字符。 

在给定正则表达式 a*b、输入 "aabfooaabfooabfoob" 和替换字符串 "-" 的情况下,
为该表达式针对匹配器调用此方法将产生字符串 "-foo-foo-foo-"。 

调用此方法将更改此匹配器的状态。如果在将来的匹配操作中使用该匹配器,则应该首先重置它。


代码示例:替换指定字符。


class ReplaceDemo{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args) {
		replace1();
		replace2();
		replace3();
	}
	//如果字符串中连续出现5个以上的数字,就将其替换成#
	public static void replace1(){
		String str = "wgxin135000001111jiewin1156xin123456guangwu";
		sop(str.replaceAll("\\d{5,}","#"));
	}
	//将叠词替换成&号
	public static void replace2(){
		String str = "wgxin0101335500001111jiew111in";
		sop(str.replaceAll("(.)\\1+","&"));
	}
	//将叠词替换成单个词,$1 表示一组叠词
	public static void replace3(){
		String str = "aaabbbbbcccccddddddefg111122234445565321";
		sop(str.replaceAll("(.)\\1+","$1"));
	}
}
/*
wgxin#jiewin1156xin#guangwu
wgxin0101&&&&jiew&in
abcdefg1234565321
*/

4、获取


获取就是将字符串中符合规则的子串取出。

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

java.util.regex 
类 Pattern
正则表达式的编译表示形式。

指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用
于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配
所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。 

因此,典型的调用顺序是:
 Pattern p = Pattern.compile("a*b");
 Matcher m = p.matcher("aaaaab");
 boolean b = m.matches();

方法:
Matcher matcher(CharSequence input) 
 创建匹配给定输入与此模式的匹配器。 
static boolean matches(String regex, CharSequence input) 
 编译给定正则表达式并尝试将给定输入与其匹配。 
static Pattern compile(String regex) 
 将给定的正则表达式编译到模式中。 
static Pattern compile(String regex, int flags) 
 将给定的正则表达式编译到具有给定标志的模式中。 
 
int end() 返回最后匹配字符之后的偏移量。
int start() 返回以前匹配的初始索引。 
String group() 返回由以前匹配操作所匹配的输入子序列。 
String pattern() 返回在其中编译过此模式的正则表达式。
Pattern pattern() 返回由此匹配器解释的模式。 
boolean matches() 将尝试将整个区域与模式匹配。
boolean find() 尝试查找与该模式匹配的输入序列的下一个子序列。

代码示例:获取字符串中3个字母的单词


需求:编写正则表达式,从字符串中获取3个字母的单词

import java.util.regex.*;
class GetDemo{
	public static void sop(Object obj){System.out.println(obj);}
	public static void main(String[] args){
		//定义字符串
		String str = "ming tian fang jia le,da jia, haha.";
		
		//设置正则表达式。从字符串中获取3个字母的单词
		String reg = "\\b[a-z]{4}\\b";
		
		//把正则表达式封装成对象
		Pattern p = Pattern.compile(reg);
		
		//把对象和字符串关联,获取匹配器对象。
		Matcher m = p.matcher(str);
		
		//其实String类中的matches方法,用的就是Pattern和Matcher对象来完成的。
		//只不过被String的方法封装后,用起来较为简单,但功能却单一。
		//find()尝试查找与该模式匹配的输入序列的下一个子序列。
		while(m.find()){
			String word = m.group();	//获取匹配后的结果
			int s = m.start();			//返回以前匹配的初始索引
			int e = m.end();			//返回最后匹配字符之后的偏移量
			sop(word+" "+s+"-"+e);
		}
	}
}

四、四种功能的选择


到底用四种功能中的哪一种?或哪几个?

思路:
1、匹配:用于只想知道该字符是对是错。
2、替换:用于将已有的字符串变成另外一个字符串。
3、切割:用于按照自定的方式将字符串变成多个字符串,获取规则以外的子串。
4、获取:用于获取符合要求的字符串子串。

代码示例1:将如下字符串转换成:我要学编程.

“我我..我..要要....学.....学学...编编...编程”


class RegexTest{
	public static void main(String[] args){
		String str = "我我...我...要要...学...学学....编编.....编程";
		
		//先把字符串中的“.”替换掉
		str = str.replaceAll("\\.+","");
		
		//在把字符串中的重叠字符去掉
		str = str.replaceAll("(.)\\1+","$1");
		System.out.println(str);
	}
}

代码示例2:对IP地址进行排序


将下列IP地址进行地址段顺序的排序。
192.168.0.100 159.52.56.101 5.50.30.2 248.54.68.4 68.67.45.05

思路:
1、按最低位的需要来给每一段都补0满足3每段至少都是3位数。
2、然后每一段都只保留3位数。
3、去除每一段的1之前的0都去掉。

import java.util.*;
class IPSort{
	public static void main(String[] args){
		String ip = "192.168.0.100 159.52.56.101 5.50.30.2 248.54.68.4 68.67.45.05";
		
		//在每一段前面都补2个0
		ip = ip.replaceAll("(\\d+)","00$1");
		
		//保留每一段的后3位数
		ip = ip.replaceAll("0*(\\d{3})","$1");
		
		//用空格切割成多段存入字符串数组中
		String[] arr = ip.split(" ");
		
		//使用集合方式把数组中的元素进行自然排序
		TreeSet<String> ts = new TreeSet<String>();
		for(String s:arr){
			ts.add(s);
		}

		//遍历集合,将每一段数前面的0都去掉
		//方法是先用\\d+获取每一段数字进行封组,
		//利用0*把第一位是0的都去掉
		for(String s:ts){
			System.out.println(s.replaceAll("0*(\\d+)","$1"));
		}
	}
}
/*
运行结果:
5.50.30.2
68.67.45.5
159.52.56.101
192.168.0.100
248.54.68.4
*/

代码示例3:对邮件地址进行校验


matches()方法返回字符串与正则表达式匹配后的结果,true或false

import java.util.regex.*;
class CheckEmail{
	public static void main(String[] args){
		String mail = "A23456@163.com.cn";
		
		//最简单最懒的校验方法,安全性极差,如1@1.1,都能匹配
		//String reg = "\\w+@\\w+(\\.\\w+)+";
		
		//稍微安全的校验方法
		String reg = "^[a-zA-Z][a-zA-Z0-9_-]{5,14}+@[a-zA-Z0-9]+(\\.[a-zA-Z]+){1,2}";
		System.out.println(mail.matches(reg));
	}
}
/*
注释:
^[a-zA-Z]			 第一位只能是a-z或A-Z,
[a-zA-Z0-9_-]{5,14}  第二位后面的可以是a-zA-Z0-9还有_-。
{5,14}+				 @前面名称长度从6到15位包括15位。
@[a-zA-Z0-9]+		 @的后面可以是a-z或A-Z或0-9
(\\.[a-zA-Z]+)		 “.”必须用\\转义,用括号把表达式进行分组复用
{1,2}				 可以复用1-2次,一般邮箱域名最多也就是二级,比如@sina.com.cn
*/


五、网页爬虫


网页爬虫简单说就是互联网程序的一种代名词,也可以指一段小程序,也可以指使用者。
特点,利用这个小程序可以获取指定的网页上的数据,例如获取邮箱,给邮箱发送广告。

URLConnection 类中的方法
InputStream getInputStream() 返回从此打开的连接读取的输入流。

代码示例:获取网页上的邮件地址

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;
class GetEmail{
	public static void main(String[] args) throws Exception{
		//getEmail();
		getWebEmail();
	}
	//获取本地文件中的email地址
	public static void getEmail() throws Exception{
		//创建字符读取流缓冲区
		BufferedReader bufr = new BufferedReader(new FileReader("email.txt"));
		String line = null;
		//String mailreg = "\\w+@\\w+(\\.\\w+)+";
		String mailreg = "\\b[a-zA-Z][a-zA-Z0-9_-]{3,14}@[a-zA-Z0-9]+(\\.[a-zA-Z]+){1,2}";
		//将表达式封装成模式
		Pattern p = Pattern.compile(mailreg);
		//读取字符流缓冲区中的一行数据
		while((line=bufr.readLine())!=null){
			//把数据与正则表达式模式匹配
			Matcher m = p.matcher(line);
			//匹配下一个
			while(m.find()){
				//返回正确匹配后的结果打印出去
				System.out.println(m.group());
			}
		}
	}
	//获取网络URL中的邮件地址
	public static void getWebEmail() throws Exception{
		URL url1 = new URL("http://edu.csdn.net/main/about/contact.shtml");
		URL url2 = new URL("http://www.douban.com/event/14146775/discussion/40108760/");
		//返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。 
		URLConnection conn = url2.openConnection();
		//从conn打开的连接读取输入流,存入字符读取流缓冲区。
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		String reg = "\\b[a-zA-Z][a-zA-Z0-9_-]{3,14}@[a-zA-Z0-9]+(\\.[a-zA-Z]+){1,2}";
		Pattern p = Pattern.compile(reg);
		//使用TreeSet集合的自然排序功能。
		TreeSet<String> ts = new TreeSet<String>();
		String line = null;
		while((line=bufIn.readLine())!=null){
			Matcher m = p.matcher(line);
			while(m.find()){
				ts.add(m.group());//添加到集合中
				//System.out.println(m.group());
			}
		}
		//遍历集合中的数据
		for(String s:ts)
			System.out.println(s);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值