java stream groupby 计算和_正则表达式


前言

在编程语言中,除了容器集合、多线程并发、IO流等等基础组件之外,还有一项专门处理字符串的“奇淫巧技”,这就是正则表达式。Perl语言正是凭借强大的正则表达式才异军突起的。

对于Java来说,自从jdk1.7之后,正则表达式也逐渐强大起来,不再是之前的半吊子了。现在的Java也非吴下阿蒙,其正则表达式也不逊于其他主流语言了。

本篇将详细描述Java正则表达式的用法、技巧等等,让你领略一下正则表达式的强大功能。

由于内容较多,整个文章分为上中下三篇。上篇内容介绍 基础替代符号; 中篇内容介绍Java的正则组件以及相关API; 下篇内容特别介绍转义字符的使用。

概述

何为正则表达式?英文称为Regular Expression。 其实这个翻译有点误导性,如果翻译成“规则表达式”,可能更好理解。

设想这么一个场景: 某一天,小舟同学在用word编辑文案,突然意识到某个单词好像拼写有误, 把Java 错写了Jave。 由于文字量比较大,手工一个个替换费时费力,好容易遗漏出错,但是好在word有自动替换功能,一键把全部Jave替换成Java即可。 但是,好景不长,小舟发现有些错误拼写成了Jave,还有些错误拼写成了Javo,还有JavqJavz, Javd等等,还有连自己都不知道拼写成什么玩意的。

现在该怎么办? 小舟设想,如果有一个功能,能把以Jav开头的,不管第四个字母是什么,全部替换成Java。比如说,Jav*,以*代替任意字母,全部替换成Java


java stream groupby 计算和_字符串_02


这种以某些特殊字符来代替另外一些常用字符的表达方式,就称为正则表达式。另外,正则表达式是不限于编程语言的,在一些基本文字软件中,都有此类功能的集成。

基础替代符

在Java中, 把正则表达式的替代符号分为以下几类:

字符匹配符与范围控制符

  1. . (点) 任意字符
  2. s (反斜杠加小写字母s) 空白字符: 回车,制表,空格,换行
  3. S (反斜杠加大写字母S) 非空白字符, 除了上面那四个空白字符
  4. w (反斜杠加小写字母w) 单词字符: 小写字母a-z, 大写字母A-Z, 数字0-9, 下划线 _
  5. W (反斜杠加大写字母W) 非单词字符,除了上面提到的那些单词字符
  1. [abc] 表示字母abc中的任意一个
  2. [a-z] 表示字母a到z中的任意一个
  3. [^abc] ^ 为取非, 除了abc, 剩下的全部字符
  4. [abc[def]] 这种写法为并集,意为abcdef中的任意一个
  5. [abc&[bce]] & 表示, 也就是交集。 abc 与上 bce, 最终结果为bc

数量控制符

上面介绍的字符匹配符和范围控制符,一般是与数量控制符组合使用,才能发挥真正威力。 而数量控制符号,分为贪婪型,勉强型,和占有型三种类型,每种类型在不同的场合有不同的用法。

贪婪型

  1. ? 表示1个或0个。换句话说,表示要不然没有,要不然只有1个
  2. * 表示0个或多个。
  3. + 表示1个或多个。
  4. {n} 表示正好n个
  5. {n,m} 表示n到m个,这是一个左闭右闭区间
  6. {n,} 表示至少n个

勉强型

相比于贪婪型,勉强型只是多了一个?:

  1. ?? 表示1个或0个。换句话说,表示要不然没有,要不然只有1个
  2. *? 表示0个或多个。
  3. +? 表示1个或多个。
  4. {n}? 表示正好n个
  5. {n,m}? 表示n到m个,这是一个左闭右闭区间
  6. {n,}? 表示至少n个

占有型

相比于贪婪型,占有型多了一个 +

  1. ?+ 表示1个或0个。换句话说,表示要不然没有,要不然只有1个
  2. *+ 表示0个或多个。
  3. ++ 表示1个或多个。
  4. {n}+ 表示正好n个
  5. {n,m}+ 表示n到m个,这是一个左闭右闭区间
  6. {n,}+ 表示至少n个

不同类型的区别

现在详细分析 贪婪型、勉强型和占有型的区别, 举例如下:


String s = "abcd&"; 			// 目标字符串
                String regular = "w+w";		// 贪婪型正则
		String regular2 = "w+?w";		// 勉强型正则
		String regular3 = "w++w";		// 占有型正则
                Matcher m = Pattern.compile(regular).matcher(s);
		// 调用方法去匹配字符串
                if(m.find()) {
		   System.out.println(m.group());
		}


分析:

  1. 贪婪型。w+, 意为匹配一个或多个单词字符,对于贪婪型,将尽量多的匹配字符,abcd全部匹配出来,直到遇到特殊字符&,匹配结束,然后用w 去匹配特殊字符&,发现并不匹配,此时将会回溯,也就是说w+只匹配abc三位,然后让最后一个w去匹配d,最终w+w匹配出结果abcd.
  2. 勉强型。对于勉强型,会尽量少的匹配字符。既然,+表示一个或多个,那就只匹配一个,所以w+?最终只匹配出一个a, 然后w再去匹配一个b,最终匹配结果为ab。
  3. 占有型。 占有型和贪婪型很像,刚开始也会尽量多的匹配字符,但是占有型没有回溯机制,遇到不匹配的内容后,将自动停止而不是向前回溯。 所以 w++,自动匹配到abcd之后,遇到了&,发现不匹配,然后自动停止了,此时还有一个w也和&不匹配,所以最终结果匹配失败,输出为空。

位置匹配符

位置匹配符,不匹配任何字符只匹配位置。也就是说,位置匹配符是零宽度的,只匹配位置。

  1. ^ 匹配一行的开始位置
  2. $ 匹配一行的结束位置
  3. b 匹配单词边界。 这里的单词指的就是, a-z, 0-9, _, 和w不同的是,b匹配的单词还包括汉字。 单词边界匹配,是匹配单词字符和非单词字符之间的位置。
  4. B 匹配非单词边界。和b,刚好相反, B 匹配单词与单词之间的位置,或者非单词与非单词之间的位置。
  5. G 匹配前一个匹配结束时的位置。

举例说明:


String


比如说只想把开头的a=124找出来,而不关心后面的a,此时就可以用位置匹配符。^匹配一行的开始位置,a= 去匹配 a=, d+匹配出124, 所以最终可正确匹配出开始位置的a=124

还有一类特殊的匹配符,虽然严格意义上来说不能算是位置匹配符,但是由于这类匹配符也是零宽度的,所以本文章把这类匹配符归为位置匹配符。 这类特殊的匹配符,称为零宽度断言匹配

  1. (?=exp) 零宽度向前匹配
  2. (?<=exp) 零宽度向后匹配
  3. (?!exp) 零宽度向前非匹配
  4. (?<!exp) 零宽度向后非匹配

举例说明2:


// 目标字符串。 只想取出2前面的abc
                String s = "abc1 abc1 abc2 abc3";
                String regular = "w{3}(?=2)";
		Matcher m = Pattern.compile(regular).matcher(s);
                while(m.find()) {
                   System.out.println(m.group());
		}


就像是$匹配字符串结尾一样,(?=2) 也是匹配字符串中为2的位置,然后再去检查(?=2)前面的w{3} 是否匹配abc, 匹配则输出结果。 之所以说是零宽度,是因为(?=2)只是起一个锚点定位作用,此时尾指针指向的不是2后面,而是c后面。

再比如说3:


String s = "aaaaaaaa";
                String regular = "aa(?=aa)";
		Matcher m = Pattern.compile(regular).matcher(s);
                while(m.find()) {
	            System.out.println(m.group());
		}


结果输出将是: aa aa aa

因为,第1次匹配,匹配到前4个a,输出aa; 第2次匹配,是从第2个a后开始向后,而不是第4个a后面,(?=aa) 只标定位置,不占有宽度; 以此类推,最终结果输出为aa aa aa

举例说明4:

对于零宽度向前匹配,和向后匹配方向相反:


// 目标字符串。 只要求输出2后面的abc
                String s = "1abc 2abc 3abc";
                String regular = "(?<=2)w{3}";
		Matcher m = Pattern.compile(regular).matcher(s);
                while(m.find()) {
			System.out.println(m.group());
		}


只要求输出2后面的abc,那么就可以用(?<=2)来进行锚点标定,匹配出其后面的字符串。

对于(?!exp) 和 (?<!exp) 这两个非匹配断言,和匹配断言正好相反。 (?!exp) 是要求匹配内容的后面,不能有指定的内容; (?<!exp) 要求匹配的前面不能有指定的内容。

模式说明符

  1. (?i) 开启大小写忽略模式,但是只适用于ASCII字符
  2. (?u) 开启utf-8编码模式
  3. (?m) 开启多行匹配模式,.不匹配空白字符
  4. (?s) 单行模式,.匹配任意字符,包括空白字符
  5. (?d) 单行模式,.不匹配空白字符

举例说明:


String test2 = "I care not whether I can succeedn"
		+ "Since in choice of the distancen"
		+ "I simply travel in wind and rainn"
		+ "I care not whether I can win loven"
		+ "Since in deep love with a rosen"
		+ "I just unbosom myself bravely"; 
String reg = "(?m)w{3}$";


在默认情况下,^ 和 $ 只匹配全部字符串的开始与结束位置,但是如果用(?m)开启多行匹配模式之后, ^ 和 $ 将匹配每一行的开始与结束位置。所以说,上面上面那个举例正则(?m)w{3}$,将会匹配出每一行的最后三个字符。