看了好些天的正则表达式,终于有时间来写一篇关于它的博客了。也是因为前段时间做标签处理的工作用到,用正则匹配标签规则,少写了不少代码。在有的地方使用正则表达式确实特别棒。参考博文 ,文中提到程序员的七种基本技能,确实各种语言,系统里几乎都有对正则的支持,虽说不用精通,但也要基本运用没问题。

元字符

元字符标识在正则表达式中有特殊含义的字符,正是由它们,正则表达式才真正存在。JAVA中支持的元字符列表有:([{\^-$|}])?*+.

  • ( ):正则组中使用
  • [ ]:字符类中表示一个字符
  • { }:数量范围标识
  • \:预定义字符类中使用
  • ^ $:边界标识
  • -:字符类中表示某个范围时使用,和"[]"配合使用
  • |:逻辑或
  • ? * +:预定义数量词
  • ^:逻辑非
  • .:点号匹配除换行符的任意字符 (这个地方任然有点疑问)

这里要特别说明一个符号&,虽然&&字符类中扮演着逻辑与运算,但却不在元字符行列中

检测工具

为了学习简单,写了一段测试代码做检测用,当然你也可以使用网上的检测工具,由于目前各个语言正则的引擎各有取舍,所以在线工具的检测结果不一定和代码检测结果相同(基本上没太大出入),但用于理解正则,还是很有用的。

简单案例:匹配5个连续的数字

正则表达式为[0-9]{5},先用开源中国的在线测试工具测试一下。待匹配的字符串为“自由12345飞翔

JAVA检测代码如下

/**
* 检测简单方法
* @param target //待查找匹配的字符串
* @param regex //匹配规则的正则表达式
*/
public static void simpletest(String target,String regex) {
Pattern p = Pattern.compile(regex);//java.util.regex.Pattern;
Matcher matcher = p.matcher(target);//java.util.regex.Pattern;
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
simpletest("自由12345飞翔", "[0-9]{5}");
//执行结果如下
12345

普通字符

所谓普通字符即为非元字符,上文中提到的元字符列表,即不是上面列表中的字符,就视为普通字符,普通字符为原样匹配

案例1,普通字符

simpletest("自123由飞12333翔", "123");
//执行结果如下
123
123

如上案例中,会去目标字符串"自123由飞12333翔"中查找123,由于123为普通字符,没有特殊含义,此时原样匹配,所以匹配到"自123由飞12333翔"中两组123

案例2,元字符

simpletest("自[]由飞翔", "[");
//执行结果如下
直接报错 Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed character class

如果要让元字符原样匹配,则需要用\转义元字符,JAVA中\\才表示普通字符串的\,所以为\\[

simpletest("自[]由飞翔", "\\[");
//执行结果如下
[

如上,通过转义,匹配到 “自[]由飞翔”中的元字符 [

而在线工具可以直接将字符读入,所以不用\\\[就表示匹配字符[,如下

案例3,普通字符&

simpletest("自&&由飞翔", "&&");
//执行结果如下
&&
simpletest("自&由&飞翔", "&");
//执行结果如下
&
&

案例3可以看出,虽然&&有特殊含义,但单独用时,不用转义,和普通字符完全相同

字符类

字符类(character class),这里这个词语是个专用名词,在JAVA API 中的Pattern类中我们可以看到字符类的一个列表。一个[]中的规则叫一个字符类,一个字符类仅匹配一个字符(一个位置)

  • [abc] a、b 或 c(简单类)
  • [^abc] 任何字符,除了 a、b 或 c(否定)
  • [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围) 数字范围也能类似表示如[0-9]代表0到9中任意一个数字
  • [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集) **等同于[a-d|[m-p]] 等同与 [[a-d]|[m-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](减去)差集

案例

simpletest("abcd", "[abc]");
//执行结果如下
a
b
c simpletest("abcd", "[^abc]");
//执行结果如下
d simpletest("abcd", "[a-zA-Z]");
//执行结果如下
a
b
c simpletest("an", "[a-d[m-p]]");
//执行结果如下
a
n simpletest("abcd", "[a-z&&[def]]");
//执行结果如下
d simpletest("abcd", "[a-z&&[^bc]]");
//执行结果如下
a
d simpletest("an", "[a-z&&[^m-p]]");
//执行结果如下
a

现在我们清楚的看出来,一个字符类,也就是[]及中间内容代表一个范围,表示匹配一个字符,中括号中包含这个字符可能出现的所有情况,由于检测工具中使用了循环查找匹配,所以输出结果会查找到多个字符打印出来

预定义字符类

预定义字符类,是正则表达式中代表一组字符的特殊表示,由\打头,如下为JAVA API 中Pattern类的预定义字符类列表

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

\d:数字:[0-9]

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

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

\S:非空白字符:[^\s]

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

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

案例

simpletest("自由12345飞翔", "\\d{5}"); //同理,\d在JAVA中需要转义
//执行结果如下
12345 simpletest("自由12345飞翔", "\\D+"); //预定义数量词稍后再说
//执行结果如下
自由
飞翔 simpletest("自由 \t飞翔", "\\s+");
//执行结果如下
` `//此处匹配到一个空格和一个制表符 simpletest("自由 \t飞翔", "\\S+");
//执行结果如下
自由
飞翔 simpletest("自由abc飞翔", "\\w+");
//执行结果如下
abc simpletest("自由abc飞翔", "\\W+");
//执行结果如下
自由
飞翔

数量词

默认数量词

正则匹配中字符都有要匹配的数量,如果没有加数量词,默认数量为1,匹配一个的意思

案例1

simpletest("自由12345飞翔", "\\D");
//执行结果如下



翔 simpletest("自由12345飞翔", "\\D+");
//执行结果如下
自由
飞翔

如上案例中\\D代表匹配查找非数字字符,默认数量词为1,所以查找到一个非数字字符后直接打印后,便进入下次查找,结果如上第一段代码

如上案例中\\D+中引入+号预定义量词,代表匹配大于等于1次的连续非数字字符,所以匹配到自由后进入下一次查找

自定义量词

用户希望匹配几次,就给定匹配次数,我这里姑且叫它自定义量词吧。由大括号,上下限数量组成,上限数量可以缺少

  • {n}:恰好n个
  • {n,}:大于等于n个
  • {n,m}:大于等于n个,小于等于m个

注意:并没有{,m}这种写法

案例

simpletest("自由12345飞翔", "\\d{2}");
//执行结果如下
12 simpletest("自由12345飞翔", "\\d{2,}");
//执行结果如下
12345 simpletest("自由12345飞翔", "\\d{2,4}");
//执行结果如下
1234 simpletest("自由12345飞翔", "\\d{,7}");
//执行结果如下
报错 Exception in thread "main" java.util.regex.PatternSyntaxException: Illegal repetition near index 1

从上面案例中我们已经看出,量词只形容最近字符的数量,大括号中可以指定具体字符的具体数量或者范围。

预定义量词

预定义量词是正则中用?,+,* 三个符号表示特定意思的量词

  • ?:一次或者零次
  • +:一次或者多次
  • *:零次或者零次以上

案例1

simpletest("自由12345飞翔", "\\d?");
//执行结果如下
//空行
//空行
1
2
3
4
5
//空行
//空行

这里要特别解释一下两个空行的产生,正则引擎去自由12345飞翔查找\\d?时,逐个字符从左到右查找,由于?表示一个或者零个,所以第一个字符匹配成功得到0个数字,也就是一个空字符,所以打印出来,而后面匹配到1个数字“1”打印出来,在匹配到1个数字“2”打印出来、、

案例2

simpletest("自由12345飞翔", "\\d+");
//执行结果如下
12345

这里匹配至少一个数字,直接匹配到5个数字“12345”输出

案例3

simpletest("自由12345飞翔", "\\d*");
//执行结果如下
//空行
//空行
12345
//空行
//空行

这里出现空行的原因和案例1中相同,因为\d*代表0次或者0次以上

边界标识符

如下为JAVA API 中Pattern类的边界标识符列表

^ :行的开头

$ :行的结尾

\b :单词边界

\B :非单词边界

\A :输入的开头

\G :上一个匹配的结尾

\Z :输入的结尾,仅用于最后的结束符(如果有的话)

\z :输入的结尾

目前并没完全弄明白所有边界标识符的用法,抱歉,仅演示几个。

案例1

simpletest("自由12345飞翔", "^\\d+");
simpletest("12345飞翔", "^\\d+");
//执行结果如下
12345

^\\d+查找行开头紧跟1次或多次数字,显然自由12345飞翔匹配失败,因为12345并非行首,而12345飞翔匹配成功得到12345

案例2

simpletest("自由12345飞翔", "^\\d+$");
simpletest("12345飞翔", "^\\d+$");
simpletest("自由12345", "^\\d+$");
simpletest("12345", "^\\d+$");
//执行结果如下
12345

\\d+前面加上行首边界,后面加上行尾边界后,该正则只能匹配到一串纯数字,且数量满足+量词

案列3

simpletest("自由12345飞翔", "\\b\\d+");
//执行结果如下
//啥也没有
simpletest("自由 12345飞翔", "\\b\\d+");
//执行结果如下
12345 simpletest("自由12345 飞翔", "\\d+\\b");
//执行结果如下
12345 simpletest("12345", "\\d+\\b");
//执行结果如下
12345 simpletest("12345", "\\b\\d+");
//执行结果如下
12345

从案例3中我们可以看出所谓的\b单词边界就是指空格或者行首行位(或许还有其他,反正匹配到一个连续的词的结束或者开始位置)

案例4

simpletest("自由12345飞翔", "\\B\\d+");
//执行结果如下
12345 simpletest("自由 12345飞翔", "\\B\\d+");
//执行结果如下
2345

\B为非单词边界,和\b恰好相反,但案例4中效果却和案例3中不是相反,不能匹配到12345,因为前面有空格,但2345前面是1,是非单词边界,所以匹配成功

正则组

至此前面,基本上把正则表达式简单运用讲完,现在我们来引入一个词正则组,正则组是用()把一组字符当做一个整体,可以通过方法将这个组匹配到的字符单独取出,同样可以通过下标引用之前匹配到的该组的字符

简单应用

案例

@Test
public void grouptest( ){ String regex="\\d(\\d+)(\\D+)";
String target="520LiLing"; Pattern p = Pattern.compile(regex);
Matcher matcher = p.matcher(target);
while (matcher.find()) {
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
System.out.println(matcher.group(3));
}
} //执行结果如下
520LiLing
20
LiLing
Exception in thread "main" java.lang.IndexOutOfBoundsException: No group 3

从简单案例中可以看出()中匹配到的数字可以用matcher.group(1)方法取出,1代表第一组,案例中第一组为(\d+),第二组为(\D+),第三组没找到,报错。而matcher.group(1)代表整个正则表达式匹配到的内容。

复杂组序

JAVA API 中Pattern类中有关于正则组顺序的介绍,当遇到复杂的正则组时,怎么来确定组的序号。

((A)(B(C)))表示一个正则表达式,A,B,C分别代表随意一个表达式

  • group(1):((A)(B(C)))
  • group(2):(A)
  • group(3):(B(C))
  • group(4):(C)

从上面的列表说明不难总结出一个规律,将正则表达式从左到右读过来,依次遇到()中的左括号(依次编号,先遇到哪一组的左括号先编号

案例

@Test
public void grouptest( ){ String regex="((\\d)(\\d+(\\D+)))";
String target="520LiLing"; Pattern p = Pattern.compile(regex);
Matcher matcher = p.matcher(target);
while (matcher.find()) {
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
System.out.println(matcher.group(3));
System.out.println(matcher.group(4));
}
} //执行结果如下
520LiLing
5
20LiLing
LiLing

案例和上文说明完全一致,1组为((\\d)(\\d+(\\D+))),2组为(\\d),3组为(\\d+(\\D+)),4组为(\\D+)

捕获组

前面已经讲过关于正则组的编号,以及引用,但这样的作用似乎还不够强大。捕获组,就是将之前的正则组通过\组序号捕获,如\1(任然需要转义\\1),再次利用。(解释起来太费劲,看案例吧)

@Test
public void grouptest( ){ String regex="(\\d+).+\\1";
String target="520Li320Ling"; Pattern p = Pattern.compile(regex);
Matcher matcher = p.matcher(target);
while (matcher.find()) {
System.out.println(matcher.group(0));
System.out.println(matcher.group(1)); }
}
//执行结果如下
20Li320
20

上面案例中,正则表达式(\\d+).+\\1 的意思就是先查找到数字标记为组1,然后跟着任意字符,跟着\\1表示捕获和组1一模一样的内容。


博文地址:

http://fromwiz.com/share/s/10jE101GxA0Y2yYf1K2Rabxk05diRP2C4AVs2DmwPX2bwgI8

本文由梦想家haima整理,请勿用于商业用途。如有疑问和建议,请违心联系

正则表达式(代码java版)的更多相关文章

  1. stl文件格式解析代码--java版

    代码是参考three.js中的stlLoader.js写的. 需要注意的地方,java中byte取值-128~127 package test_stl.test_entry; import java. ...

  2. 如何写出无法维护的代码(JAVA版)

    程序命名(针对那些不能混淆的代码) 容易输入的名字.比如:Fred,asdf 单字母的变量名.比如:a,b,c, x,y,z,或者干脆上中文比如(阿隆索肯德基) 有创意地拼写错误.比如:SetPint ...

  3. 【剑指offer】Java版代码(完整版)

    原文地址:https://blog.csdn.net/baiye_xing/article/details/78428561 一.引言 <剑指offer>可谓是程序猿面试的神书了,在面试中 ...

  4. java版微信公众平台自定义菜单创建代码实现

    微信公众平台自定义菜单创建代码实现—java版 搞了两天的自定义菜单,终于搞定了,现在分享下心得,以便后来者少走弯路...... 好了,先看先微信官方的API 官方写的很详细,但是我看完后很茫然,不知 ...

  5. Jenkins日常运维笔记-重启数据覆盖问题、迁移、基于java代码发版(maven构建)

    之前在公司机房部署了一套jenkins环境,现需要迁移至IDC机房服务器上,迁移过程中记录了一些细节:1)jenkins默认的主目录放在当前用户家目录路径下的.jenkins目录中.如jenkins使 ...

  6. 趣味编程:CPS风格代码(Java 8,Functional Java版)

    CPS风格代码(Java 8版) package fp; import java.util.function.IntConsumer; public class CPS { static int ad ...

  7. 剑指offer】Java版代码(完整版)

    转自:剑指offer]Java版代码(完整版) 转自:[剑指offer] JAVA版题解(完整版)

  8. 《算法导论》第二章demo代码实现(Java版)

    <算法导论>第二章demo代码实现(Java版) 前言 表示晚上心里有些不宁静,所以就写一篇博客,来缓缓.囧 拜读<算法导论>这样的神作,当然要做一些练习啦.除了练习题与思考题 ...

  9. java版gRPC实战之一:用proto生成代码

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

随机推荐

  1. Redis主从复制(读写分离)

    主从复制(读写分离):读在从库读,写在主库写. 主从复制的好处:避免redis单点故障构建读写分离架构,满足读多写少的需求. 主从架构: 操作(启动实例,在一台机器上启动不同的实例,进行伪主从复制): ...

  2. Asp.net Core启动流程讲解(四)

    Asp.net Core内 DI(DependencyInjection)贯穿了项目的始终,要学习Asp.net Core就无法越过DI. 下面讲解一下DI在Asp.Net Core内的流程 asp. ...

  3. js对象数组新增、修改时的验证是否重复的逻辑

    JS代码: // 定义数据集合 const persons = [ { id: 1, name: '张三' }, { id: 2, name: '李四' } ] console.log('') con ...

  4. 华为SEO搜索引擎主管招聘内容

    http://www.wocaoseo.com/thread-166-1-1.html 华为SEO搜索引擎主管招聘内容: 职位职责 1. 提出全站的SEO策略和实施计划,推动和监督计划实施:负责提升各 ...

  5. google protocol buffer——protobuf的编码原理二

    这一系列文章主要是对protocol buffer这种编码格式的使用方式.特点.使用技巧进行说明,并在原生protobuf的基础上进行扩展和优化,使得它能更好地为我们服务. 在上一篇文章中,我们主要通 ...

  6. 【小白学AI】GBDT梯度提升详解

    文章来自微信公众号:[机器学习炼丹术] 文章目录: 目录 0 前言 1 基本概念 2 梯度 or 残差 ? 3 残差过于敏感 4 两个基模型的问题 0 前言 先缕一缕几个关系: GBDT是gradie ...

  7. 初学WebGL引擎-BabylonJS:第0篇-起因

    学习WebGL的BabylonJS是在一次偶然的情况下进行的,主要为了满足个人对全栈开发的欲望. 言归正传,下面开始简单说说相关过程 WebGL是什么?WebGL是基于html的客户端页面技术,基于h ...

  8. 【转】Android 音量键+电源键 截屏代码小结

    http://104zz.iteye.com/blog/1752961 原文地址:http://blog.csdn.net/hk_256/article/details/7306590 ,转载请注明出 ...

  9. python3笔记-读取ini配置文件

    在代码中经常会通过ini文件来配置一些常修改的配置.下面通过一个实例来看下如何写入.读取ini配置文件. 需要的配置文件是: [path] back_dir = /Users/abc/PycharmP ...

  10. SSM框架之mybatis极速入门!