javase正则表达式入门

本文深入探讨了Java中正则表达式的使用,包括基础匹配、替换、分组、范围匹配、边界匹配等内容,并通过实例展示了如何在实际场景中应用正则表达式。同时,介绍了代码行统计、邮件地址抓取、文件夹内Java文件代码行数统计以及不同类型的数量词在正则表达式中的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

j2se中一个重要的部分正则表达式很强大,它在处理字符串匹配时强大有余啊。主要涉及到java.util.regex包中的Pattern类与Matcher类。

下面从代码中,走入正则表达式

代码片段一,入门级的:

	private static void testRegExpAccidence() {
		//正则表达示第一个内容,点.可以替换任何一个字符。所以下面的应该是true。match(...)代码,是否符合有三个字符.
		sop("abc".matches("..."));
		
		//正则表达示2,reaplaceAll第一参数支持正则,而\\d代表的是数字,就是说把所有数字替换成&。
		sop("abc1234abc".replaceAll("\\d","&"));
		
		//正则表达示3,compile的玩意。一般需要先把正则规则编译成Pattern,而Pattern无构造方法,只能通过compile方法
		Pattern pat = Pattern.compile("[abc]{3}");
		Matcher mat = pat.matcher("abc"); //通过编译出来的Pattern去匹配一个字符,返回的一个是匹配的结果用Matcher对象
		sop(mat.matches());//其实也可以直接用"abc".matches("[abc]{3}") 来判断字符串,是否符合[abc]{3}正则表达示
		
		sop("------------. * + ?作用 --------------------");
		//认为. * + ?的作用。
		sop("a".matches(".")); //.匹配任意一个
		sop("aaaa".matches("a*")); //X* 代表X出现0次或多次
		sop("aaaa".matches("a+")); //X+ 代表X出现1次或多次
		sop("aaaa".matches("a?"));//false  //X? 代表X出现1次或0次
		sop("".matches("a*"));
		sop("".matches("a+")); //false
		sop("".matches("a?"));
		sop("a".matches("a*"));
		sop("a".matches("a+"));
		sop("a".matches("a?"));
		sop("2343902594".matches("\\d{3,100}")); // \\d代表数字然后{3,100}代表至少出现3次,至多出现100次
		sop("192.168.0.aaa".matches("\\d{3}\\.\\d{3}\\.\\d{3}\\.\\d{3}")); //false,其它未标识全部是true
		sop("192".matches("[0-2][0-9][0-9]"));
		
		sop("---------------范围的匹配------------------------");
		sop("a".matches("[abc]")); //true,[abc]代表,或者a或者b或者c,取三个之中的任意一个
		sop("a".matches("[^abc]")); //false, [^abc]代表,不得是a或b或c,即不得是三个中的任意一个
		sop("A".matches("[a-zA-Z]")); //true 范围[a-zA-Z]代表是a-z或A-Z中的任意一个
		sop("A".matches("[a-z]|[A-Z]"));//true,范围[a-z]|[A-Z]也是代表a-z或A-Z中的任意一个
		sop("A".matches("[a-z[A-Z]]"));//true,范围[a-z[A-Z]]也是代表a-z或A-Z中的任意一个
		sop("R".matches("[A-Z&&[RFG]]"));//true 范围[A-Z&&[RFG]]代表取A-Z与RFG两个范围交集RFG中的任意一个
		
		sop("---------------认识反斜杠s、反斜杠w、反斜杠d、反斜杠------------------------");
		//\s \w \d \  \s代表空格 \w代表一个单词(a-z0-9_A-Z) \d代表数字 
		// \ 通常和转义一起用,如\t\r\n之类的,若只当成一个普通字符\,则需要再转义一次 \\。
		sop(" \n\r\t".matches("\\s{4}")); //\s,转义需要再转义一次即\\s,代表四个空格。
		sop(" ".matches("\\S")); // \S,代表是\s的非取反,即不是空格(非空格)。 规则一般大写是小写的语义相反如\D是非数字  结果肯定false。
		sop("a_8".matches("\\w{3}")); // \\w代表单词中的字母,{3}代表出现三次。结果肯定是true了。
		//sop("\\".matches("\\")); // 注意这里肯定会报错,因为在matches正则中,先java解释两个\\是一个转义,
		           //  但一个转义让正则去解释肯定要报错,它像java一样要再用一个转义来转义它,证明它是一个普通的反斜杠。
		sop("\\".matches("\\\\"));
		
		sop("---------------认识boundary边界 ^及$ \\b------------------------");
		sop("hello sir".matches("^h.*")); //^不在中括号[]中,代表以什么开头,如这里是代表以h开头
		sop("hello sir".matches(".*ir$")); //$代表以什么结尾,如这里是以ir结尾
		// 这里有\\b代表 单词边界,可以以空格  \t或\r \n等来隔开。
		sop("hello sir".matches("^h[a-z]{1,3}o\\b.*"));  //这里的.要加上,代理单词边界后面还是字符,1个或多个。
		sop("hellosir".matches("^h[a-z]{1,3}o\\b*"));  //就是false了。
		
		
		sop("---------几个小练习------------");
		sop("aaa 8888c".matches(".*\\d{4}.")); //肯定是true,因为.*代表任意出现0次或多次
		sop("aaa 8888c".matches(".*\\b\\d{4}.")); //肯定是true,因为.*代表任意出现0次或多次
		sop("aaa8888c".matches(".*\\d{4}."));
		sop("aaa8888c".matches(".*\\b\\d{4}."));//这个就false了,因为\\b是单词边界,后面又跟着\d{4}肯定没戏。
		
		sop("chenssadf@aio7.com".matches("\\w+@\\w+\\.\\w+"));  //这个虽然可以用,但似乎对前面的@单词可以用.或-的没有加上哦。
		sop("-chenssadf@aio7.com".matches("[\\w.-]+@[\\w.-]+\\.\\w+"));
	}


代码片段二:掌握Matcher类的 find、start、end、lookingAt方法:

	private static void testFindAndLookingAt() {
		sop("---------------------matcher.find 与 matcher.lookingAt match.reset等方法测试--------------------------");
		Pattern p = Pattern.compile("\\d{3,5}");
		Matcher match = p.matcher("234-32132-4321-00");
		sop(match.matches()); //matches方法与find方法相互操作是有影响的,因为两者查看完会记录出下个要操作的指针位置。
		match.reset();       //比如这里没有reset重置指针位置时,matches方法虽然匹配失败false,但它吐进去的4个字符不会吐出来
		sop(match.find());   //这样,这里的第一个find从32132开始查了,就不从头了。(除非前面有reset重置)。所以接下来最后两上肯定是false了。
		sop(match.start()+"--"+match.end()); //上面的find必须返回是true ,然后start是返回本次找到数据的第一位置,这里肯定是0,最后一个位置3(3是不包括)
		sop(match.find());
		sop(match.start()+"--"+match.end()); // start肯定就是4,end就是9了。
		sop(match.find());
		sop(match.start()+"--"+match.end());
		sop(match.find());
		//sop(match.start()+"--"+match.end()); //注意这里,不能打印的。因为上面的find返回的是false,根本没有找到。所以start或end方法
		                                    //会报IllegalStateException: No match available
		
		sop(match.lookingAt()); //这下面的lookingAt打印永远都是true,因为lookingAt永远每次都是重新重头打开查。
		sop(match.lookingAt());
	}


代码片段三:掌握强大的replace、replaceAll、appendReplacement方法

	private static void testReplacement() {
		sop("---------------------------match.replece及相关的替换测试---------------------");
		//CASE_INSENSITIVE 大小写不敏感即能吃呢   Pattern.CANON_EQ 默认的方法即是敏感必须相同
		Pattern p = Pattern.compile("java",Pattern.CASE_INSENSITIVE);  
		//怎么把下面一串乱七八槽的java替换掉呢。比如只替换正则中规定的java
		Matcher match = p.matcher("Java java JAva JAVa ILOVEJAVA youhateJaVa asdfasl;kdfj");
		//sop(match.replaceAll("SOP"));  //可以将java全部替换成SOP,或只把小写java替换都可以。
		//现有有个需求是,把单次数出现的java大小写均可,替换成SOP,双次数出现的替换成HELP。可以通过while循环find来搞
		
		int count = 0;
		StringBuffer buf = new StringBuffer();  //这里不可以写StringBuilder,因为matcher后面的方法不支持Builder
		while(match.find()){
			if(0 == count++%2){
				match.appendReplacement(buf, "HELP");
			}else{
				match.appendReplacement(buf, "SOP");
			}
		}
		sop(match.appendTail(buf)); //把后面没有find上的尾巴asdf等字符添加 进去。
	}


代码片段四:url中抓取邮件地址:

/**
	 * 通过向一网站,发送http请求,把请求回响回来的网页内容进行解析,分析出来e-mail出来。
	 */
	private static void testUrlFetchEmail() throws Exception {
		sop("-----------------用URL发送一个请求给网站,对返回的内部进行抓取e-mail地址出来----------------------");
		//http://dzh.mop.com/whbm/20060220/3/S7gSlI706d198a73.shtml
		URL url = new URL("http://dzh.mop.com/whbm/20060220/3/S7gSlI706d198a73.shtml");
		sop(url.getContent());
		sop(url.getQuery());
		InputStream is = url.openStream();
		BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
		Pattern p = Pattern.compile("([\\w.-]+)@([\\w.-]+)\\.\\w+");
		//Pattern p = Pattern.compile("^([\\w.-]+)@([\\w.-]+)\\.\\w+"); //这里把^加上去就挂了,代表这个字符串是以邮件开头
		String line = null;  //从URL中读出来的字符行,肯定不会是以邮件开头的,至少都是<br>或<P>或<dir>之类的,不会以邮件开头的吧。
		while((line=br.readLine())!=null){
			Matcher ma = p.matcher(line);
			while(ma.find()){  //group必须与find一起来联系。否则报:java.lang.IllegalStateException: No match found
				sop(ma.group()+"这家伙用户名:"+ma.group(1)+",用的公司域名:"+ma.group(2));
			}
			//sop(line);
			//java.lang.IllegalStateException: No match found
		}
	}


代码片段五:代码行统计

	/**
	 * 若是文件夹则递归,若是普通的.java文件则传给统计代码方法。
	 * @param dir 是传入到此方法中 目录的文件。如想查到D:\test文件夹(会以递归查所有文件)中所有代码
	 * @throws Exception
	 */
	private static void parseDirCodeNum(File dir) throws Exception{
		File[] files = dir.listFiles(new FilenameFilter(){
			@Override
			public boolean accept(File dir, String name) {
				File tempFile = new File(dir,name);
				if(tempFile.isFile()){  //若是个普通文件的话,则过滤一切不是.java的文件,文件夹为true,以便再次递归
					if(name.endsWith(".java")){
						return true ;
					}else{
						return false;
					}
				}
				return true;
			}
		});
		
		for(File f : files){
			if(f.isDirectory()){
				parseDirCodeNum(f);
			}else{
				parseFileDirNum(f);  //是普通java文件则解析出代码行数
			}
		}
	}
	/**
	 * 统计代码行的方法。
	 * @param f
	 * @throws Exception
	 */
	private static void parseFileDirNum(File f) throws Exception{
		BufferedReader br = new BufferedReader(new FileReader(f));
		boolean isComment = false ;
		String line = null ;
		while((line = br.readLine())!=null){
			line = line.trim();  //一开始就去掉取出来行中的 空格,因为对于注释前面一般有\t影响stratsStart
			if(line.startsWith("/*")&&line.endsWith("*/")){ //只有一行注释
				commentLine++;
			}else if(line.startsWith("/*")){ //可能出现了多行注释了,那么就标识注释isComment开始了,true了。
				commentLine++;
				isComment = true;
			}else if(isComment){ //要是标识开始了,未标识出false统统认为是注释行。当然若注释结束了*/了,则去掉标识。
				commentLine++;
				if(line.endsWith("*/")){  //这里没有问题,因为注释中,不能含有*/的。
					isComment = false;
				}
			}else if(line.startsWith("//")){
				commentLine++;
			}
			else if(line.matches("^[\\s&&[^\\n]]*$")){  //空白行,必须是空白开头且不能回车
				whitespaceLine++;
			}else {
				normalLine++;
			}
		}
		
		
	}


代码片段六:掌握Greedy Reluctant Possessive数量词的区别:

/**
	 * 测试Greedy Reluctant Possessive数量词
	 * Greedy是贪吃 肯定是一上来,就以最多的吃掉,若不满足且可退时,再退出一个再匹配...
	 * Reluctant是 勉强不情意的。 一上来,就吃掉最小的,若不满足且可再吃时,就再吃一个匹配...
	 * Possessive是 
	 */
	private static void testGreedyReluctantPossessive() {
		sop("-------------测试 Greedy Reluctant Possessive数量词-----------------------");
		Pattern gp = Pattern.compile(".{3,10}[0-9]");  //Greedy方式
		Pattern rp = Pattern.compile(".{3,10}?[0-9]");  //Reluctant方式  主要的与Greedy区别是多出一个?
		Pattern pp = Pattern.compile(".{3,10}+[0-9]");  //Possessive方式 主要的与Greedy区别是多出一个+
		Matcher gm = gp.matcher("asdf8sdfa9"); //通过Greedy匹配出来的匹配器
		Matcher rm = rp.matcher("asdf8sdfa9"); //通过Reluctant匹配出来的匹配器
		Matcher pm = pp.matcher("asdf8sdfa9"); //通过Possessive匹配出来的匹配器
		
		if(gm.find()){  //因为gm是一次吃掉10个发现后面要求一个数字所以就退回了一个,这样正好满足找到了。
			sop(gm.start()+"  "+gm.end()); //返回0  10
		}else{
			sop("找不到匹配的");
		}
		
		if(rm.find()){ //因为rm是一次吃掉3发现后面要求一个数字,就再吃掉一个,这样正好满足找到了。
			sop(rm.start()+"  "+rm.end()); //返回0  5
		}else{
			sop("找不到匹配的");
		}
		
		if(pm.find()){ //因为pm是一次性吃掉10个,然后正规要求后面一个数字,但它不吐出来(看来更贪婪,难怪是独占式的)。找不到了。
			sop(pm.start()+"  "+pm.end());
		}else{
			sop("找不到匹配的");  //返回找不到
		}
		
		
	}


代码片段七相关测试代码:

        private static long normalLine ;
	private static long commentLine ;
	private static long whitespaceLine ;
	
	public static void main(String[] args) throws Exception{
		testRegExpAccidence();
		testFindAndLookingAt();
		testReplacement();
		
		sop("-----------------match.group及相关的分组测试--------");
		Pattern p = Pattern.compile("([\\w.-]+)@([\\w.-]+)\\.\\w+");
		Matcher m = p.matcher("-chenshufei2@sina.com");
		while(m.find()){
			sop(m.group());
			sop(m.group(1)); //可以得出用户名-chenshufei2
			sop(m.group(2)); //可以得出域公司名sina 
			//sop(m.group(3));  会报出一个java.lang.IndexOutOfBoundsException: No group 3
			//从此 可以验证出,分组就是看正则中小括号(出现的位置,而上面的没有第三个(,所以会报异常。
		}
		
		//testUrlFetchEmail(); //D:/java_setup_program_file/eclipse/design_pattern/EnhanceJava
		String dirStr = "D:\\360Rec";
		File dir = new File(dirStr);
		parseDirCodeNum(dir);
		sop("解析文件时,共有normalLine::"+normalLine+"  ,commentLine::"+commentLine+"  ,whitespaceLine::"+whitespaceLine);
		
		
		testGreedyReluctantPossessive();
	}
public static void sop(Object obj){
		System.out.println(obj);
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值