正则表达式(RegularExpressions)

1、正则表达式

正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。

1.1、概念

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

1.2、正则表达式可以做什么

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

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

1.3、正则表达式的特点

  1. 灵活性、逻辑性和功能性非常的强;
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。(缺点)
    由于正则表达式主要应用对象是文本(字符串),因此它在各种文本编辑器场合都有应用,小到著名编辑器EditPlus,大到Microsoft Word、Visual Studio等大型编辑器,都可以使用正则表达式来处理文本内容。

其他相关概念
引擎
正则引擎主要可以分为两大类:一种是DFA,一种是NFA。这两种引擎都有了很久的历史(至今二十多年),当中也由这两种引擎产生了很多变体!于是POSIX的出台规避了不必要变体的继续产生。这样一来,主流的正则引擎又分为3类:
一、DFA,二、传统型NFA,三、POSIX NFA。

1.4、符号

正则表达式由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义。【请查阅相关元字符表】
java.util.regex.Pattern

1.5、用途

(1)字符串匹配(字符匹配)
(2)字符串查找
(3)字符串替换
例如:
(1)手机号码是否符合规范等
(2)邮箱地址是否正确
(3)IP地址是否正确

1.6、相关类

java.lang.String
java.util.regex.Pattern
java.util.regex.Matcher

2、案例演示

(1)QQ校验
(2)手机号码验证
(3)邮箱地址校验 dsfd@fsd.fds
(4)获取由三个字符组成的单词

2.1、案例1

public class qq {
	public static void main(String[] args) {
//		 String.matches(String regex);
		 String regex = "[1-9][0-9]{4,14}";//正则表达式

		 boolean b = "204227136".matches(regex);
		 System.out.println(204227136+" : "+b);
	}
//	需求:定义一个功能对QQ号进行校验
//	要求:长度5-15,只能是数字,0不能开头
	public static void checkQQ(String qq) {
			int len = qq.length();
			if (len >= 5 && len <= 15) {
				if (!qq.startsWith("0")) {
					try {
						long l = Long.parseLong(qq);
						System.out.println(l + " 正确!");
					} catch (NumberFormatException e) {
						System.out.println(qq + ":含有非法字符!");
					}
				} else {
					System.out.println(qq + " : 不能以0开头");
				}
			}else{
				System.out.println(qq+" 长度错误!");
			}
		}
}
正则表达式(常见的规则)可查看JDK API 文档
字符类
[abc]a、b 或 c(简单类) (某一位上只能是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](并集)
[a-z&&[def]]d、e 或 f(交集)
[a-z&&[^bc]]a 到 z,除了 b 和 c:[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]
边界匹配器
^行的开头
$行的结尾
\b单词边界
\B非单词边界
\A输入的开头
\G上一个匹配的结尾
\Z输入的结尾,仅用于最后的结束符(如果有的话)
\z输入的结尾
Greedy 数量词
X?X,一次或一次也没有 (举例演示)
X*X,零次或多次
X+X,一次或多次
X{n}X,恰好 n 次
X{n,}X,至少 n 次
X{n,m}X,至少 n 次,但是不超过 m 次

2.2、案例2

	public class RegexDemo2 {
		public static void main(String[] args) {
			String str ="aooooooooooooooooooooooooooob";
			//String reg = "ao?b";
			//String reg = "ao{4}b";
			//String reg = "ao{4,}b";
			//String reg = "ao{4,6}b";
			//String reg = "ao+b";
			String reg = "ao*b";
			boolean b = str.matches(reg);
			System.out.println(b);
	}
}

2.3、案例3

需求:手机号码验证(必讲)
正则表达式对字符串的常见操作:
1、匹配
其实使用的就是String类中的matches()方法
2、切割
其实使用的就是String类中的split()方法
3、替换
其实使用的就是String类中的replaceAll()方法
4、获取
/**

  • 需求:手机号码验证
    • 正则表达式对字符串的常见操作:
    • 1、匹配
    • 其实使用的就是String类中的matches()方法
    • 2、切割
    • 其实使用的就是String类中的split()方法
    • 3、替换
    • 其实使用的就是String类中的replaceAll()方法
    • 4、获取

*/

public class RegexDemo3 {
   public static void main(String[] args) {
   	// method1();
   	// method2();
   	 method3();
//		method4();
   }

   /*
    * 演示匹配 匹配手机号码是否正确 (必讲) matches()方法
    */
   public static void method1() {
   	String tel = "15804901111";
   	String regex = "1[358][0-9]{9}";
   	// String regex = "1[358]\\d{9}";

   	boolean b = tel.matches(regex);
   	System.out.println(tel + " : " + b);
   }

   /*
    * 切割 split()方法 组:((A)(B(C))) 选讲
    */
   public static void method2() {
   	String str1 = "zhangsan xiaoqiang zhaoliu";
   	// String str2 = "zhangsan.xiaoqiang.zhaoliu";
   	// String str3 = "zhangsanttttxiaoqiangmmmmmmzhaoliu";//组

   	String[] names = str1.split(" ");
   	// String [] names = str1.split(" +");
   	// String [] names = str2.split("\\.");
   	// String [] names = str3.split("(.)\\1+");//组

   	for (String name : names) {
   		System.out.println(name);
   	}
   }

   /*
    * 替换 replaceAll()方法
    */
   public static void method3() {
   	String str = "zhangsanttttxiaoqiangmmmmmmzhaoliu";

   	str = str.replaceAll("(.)\\1+", "$1");//
   	System.out.println(str);

   	String tel = "15856567788";
   	tel = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
   	System.out.println(tel);
   }
   
   public static void method4(){
   	String str = "da jia hao,ming tian bu fang jia";
   	//String regex = "[a-z]{3}";
   	String regex = "\\b[a-z]{3}\\b";//  单词边界 \\b
   	
   	//1、将正则规则进行对象的封装
   	Pattern p = Pattern.compile(regex);
   	//2、通过正则对象获取匹配器对象
   	Matcher m = p.matcher(str);
   	//3、使用Matcher对象的方法对字符串进行操作。
   	// 既然要获取三个字母组成的单词
   	// 查找find();
//    	m.find();
//    	System.err.println(m.group());//获取匹配的子序列
   	System.out.println(str);
   	while(m.find()){
   		System.out.println(m.group());//获取匹配的子序列
   		System.out.println(m.start()+" : "+m.end());
   	}
   }
}
/*
 * method4
 * 业务:获取由三个字符组成的单词
 * 见API

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

将正则规则进行对象的封装
Pattern p = Pattern.compile(“a*b”);
通过正则对象的matcher方法字符串相关联。获取要对字符串操作的匹配器对象Matcher。
Matcher m = p.matcher(“aaaaab”);
通过Matcher匹配器对象的方法对字符串进行操作。
boolean b = m.matches();
*/

注意:(.)\1+

"(.+)(.*?)\\1"表示什么意思呢?

.表示单个字符+表示一个或多个, .+表示一个或多个单字符。

()是什么意思呢?

举个例子  

 ( a.)+  表示一个或多个a.此处.表示字符      

 a(.+)表示a一个或多个.

区别在于第一个表示的a和.数量是相同的

第二行只有一个a

所以()的意思就是限定+修饰的字符

(.*?)表示任意多个单字符(包括没有) 这个?是什么意思呢?表示最小的匹配

\\1表示第一个括号中匹配到的字符



字符串a2a2a2中"(.+)(.*?)\\1" 改变后为a2a2  即$1匹配和\\1是相同的只能是a2 $2就匹配为中间的a2

若字符串为1111则$1和\\1匹配的就是11和11$2匹配到的就是空的 

$1$2分别表示第一个()和第二个()匹配的值

2.4、案例4

需求1:治疗口吃:我我…我我…我要…要要要要…要要要要…学学学学…编编编编编…程程
需求2:对ip地址排序
需求3:对邮件地址校验
import java.util.Arrays;
import java.util.TreeSet;
/**
*

  • 需求1:治疗口吃:我我…我我…我要…要要要要…要要要要…学学学学学…编编编编编…程程程程
  • 需求2:对ip地址排序 需求
  • 需求3:对邮件地址校验
    */
public class RegexTest {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// test1();
		test2();
		// test3();
	}

	/**
	 * 1、治口吃
	 */
	public static void test1() {
String str = "我我...我我...我要...要要要要...要要要要...学学学学学...编编编编编....程程程程";

		// 1.将字符串中.去掉。用替换
		str = str.replaceAll("\\.+", "");
		System.out.println(str);

		// 2.替换叠词
		str = str.replaceAll("(.)\\1+", "$1");
		System.out.println(str);
	}

	/**
	 * 2、对ip地址排序 (看情况讲)
	 * 
	 * 192.168.10.55 127.0.0.1 3.3.3.3 105.7.11.65
	 */
	public static void test2() {
		String ipstr = "192.168.10.55 127.0.0.1 3.3.3.3 105.7.11.65";

		// 方式二
		// 1.为了让ip可以按照字符串顺序比较,只要让ip的每一段的位数相同。
		// 所以,补零,按照每一位所需做多0进行补充,每一段都加两个0.
		ipstr = ipstr.replaceAll("(\\d+)", "00$1");
		System.out.println(ipstr);
		//00192.00168.0010.0055 00127.000.000.001 003.003.003.003 00105.007.0011.0065
		// 然后每一段保留数字3位
		ipstr = ipstr.replaceAll("0*(\\d{3})", "$1");
		System.out.println(ipstr);

		// 方式一
		// 1.将ip地址切出
		String[] ips = ipstr.split(" +");

		TreeSet<String> ts = new TreeSet<String>();
		
        // Arrays.sort(ips);
		for (String ip : ips) {
			System.out.println(ip);
			ts.add(ip);
		}

		for (String ip : ts) {
		System.out.println(ip);
			System.out.println("---"+ip.replaceAll("0*(\\d+)", "$1"));
		}
	}
	
	/**
	 * 对邮箱地址校验 (必讲)
	 */
	public static void test3(){
		String mail = "abc1@sina.com";
		String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3})+";
		
		//regex = "\\w+@\\w+(\\.\\w+)+";// 1@1.1
		
		boolean b = mail.matches(regex);
		
		System.out.println(mail+" : "+b);
		
	}
}

2.5、案例5

需求:爬虫 (需要自做一个静态的网页包含邮箱地址)-- (看情况讲解/代码直接讲解演示)
网页爬虫:其实就是一个程序用于在互联网中获取符合指定规则的数据。 爬取邮箱地址

版本一:本地版 – mail.html放在本地硬盘目录即可
版本二:网络版 – mail.html 部署在tomcat中 演示 模拟网络获取网页中的邮箱地址
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
*
*

  • 网页爬虫:其实就是一个程序用于在互联网中获取符合指定规则的数据。 爬取邮箱地址
  • (需要自做一个静态的网页包含邮箱地址)
    */
public class RegexTest2 {
	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		List<String> list = getMails();
		for (String mail : list) {
			System.out.println(mail);
		}
	}

	public static List<String> getMails() throws IOException {
		List<String> list = new ArrayList<String>();
		// 1.读取源文件
		BufferedReader br = new BufferedReader(new FileReader("c:\\mail.html"));

		// 2.对读取的数据进行规则的匹配。从中获取符合规则的数据。
		String mailRegex = "\\w+@\\w+(\\.\\w+)+";

		Pattern p = Pattern.compile(mailRegex);
		String line = null;

		while ((line = br.readLine()) != null) {
			Matcher m = p.matcher(line);
			while (m.find()) {
				// 3.将符合规则的数据存储到集合中
				list.add(m.group());
			}
		}

		return list;
	}

	public static List<String> getMailsByweb() throws IOException {

		List<String> list = new ArrayList<String>();
		// 1.读取源文件
		//BufferedReader br = new BufferedReader(new FileReader("c:\\mail.html"));
        URL url = new URL("http://12.168.1.100:8080/myweb/mail.html");
//URL url = new URL("http://renshi.nwpu.edu.cn/rczp/index.html");
URL url = new URL("http://www.lhtit.com/Contactus/index.html");
        BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
        
        // 2.对读取的数据进行规则的匹配。从中获取符合规则的数据。
		String mailRegex = "\\w+@\\w+(\\.\\w+)+";

		Pattern p = Pattern.compile(mailRegex);
		String line = null;

		while ((line = br.readLine()) != null) {
			Matcher m = p.matcher(line);
			while (m.find()) {
				// 3.将符合规则的数据存储到集合中
				list.add(m.group());
			}
		}
		return list;
	}
}

2.6、案例6

需求:统计java文件中的 空行 代码行 注释 数量 (看情况讲解/代码直接讲解演示)
package com.sxt.javaregex.demo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**

  • 统计指定文件夹下的java源文件的代码行数

*/

public class CodeCounter {
	
	static long normalLines = 0;//正常行
	static long commentLines = 0;//注释行
	static long whiteLines = 0;//空白行
	
	public static void main(String[] args) {
		//1.获得文件夹
		File f = new File("C:\\javacode");
		
		//2.获得子文件夹中的 java文件
		File[] codeFiles = f.listFiles();
		for(File child : codeFiles){
			if(child.getName().matches(".*\\.java$")) {// $代表结尾
				parse(child);
			}
		}
		
		System.out.println("normalLines:" + normalLines);
		System.out.println("commentLines:" + commentLines);
		System.out.println("whiteLines:" + whiteLines);
		
	}

	private static void parse(File f) {
		BufferedReader br = null;
		boolean comment = false;
		try {
			br = new BufferedReader(new FileReader(f));
			String line = "";
			while((line = br.readLine()) != null) {
				line = line.trim();//去掉 注释前的 tab换行
				if(line.matches("^[\\s&&[^\\n]]*$")) {//匹配空白行
					whiteLines ++;
				} else if (line.startsWith("/*") && !line.endsWith("*/")) {//匹配注释开始
					commentLines ++;
					comment = true;	
				} else if (line.startsWith("/*") && line.endsWith("*/")) {
					commentLines ++;
				} else if (true == comment) {
					commentLines ++;
					if(line.endsWith("*/")) {//匹配注释结束
						comment = false;
					}
				} else if (line.startsWith("//")) {
					commentLines ++;//匹配单行注释
				} else {
					normalLines ++;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(br != null) {
				try {
					br.close();
					br = null;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

总结
正则表达式 不要刻意去记 要理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jsxllht

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值