📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍
文章目录
- 写在前面的话
- [JS 正则用法](#JS 正则用法)
-
- [JS - 快速入门](#JS - 快速入门)
- [JS - String 的正则用法](#JS - String 的正则用法)
- [JS - RegExp 的正则用法](#JS - RegExp 的正则用法)
- [Java 正则用法](#Java 正则用法)
- [Oracle 正则用法](#Oracle 正则用法)
- 总结陈词
写在前面的话
正则表达式,英文全称 Regular Expression
,一个常见的技术,大多数人可能都不陌生,但可能没花时间系统得去了解它。
它具备如下特征:
- 定义了字符串的模式,可以用来搜索、编辑、验证、或处理文本;
- 并不仅限于某一种语言,但是在每种语言中有细微的差别;
- 适用范围非常广,无论是前后端代码开发,还是日常的IDEA、Notepad++等工具的查找/替换,无处不在;
我们将正则表达式称为"字符串处理的杀手锏",没有它处理不了的字符串需求。
下文简单介绍一下正则在不同语言的基础使用,由于篇幅所限,其他内容将在后续篇章补充。
JS 正则用法
Tips:正则表达式是由一个字符序列形成的搜索模式,可以是一个简单的字符或一个更复杂的模式。可以用这个搜索模式来描述你要查询的内容,用来搜索或进行文本替换操作。
JS - 快速入门
如何在JS中使用正则表达式
通过在一对分隔符之间放置表达式模式的各种组件,就可以构建正则表达式
对于JS,分隔符是正斜杠 (/) 字符,可以直接书写正则,格式形如:/expression/
语法:/正则表达式主体/修饰符(可选)
例如:var pattern = /\w+/;
当然,也可以使用 RegExp 对象构建正则表达式。
String 和 RegExp对象都定义了使用正则表达式的方法。
浏览器打开F12,直接写正则
经典Demo
Tips:本例子涉及到小括号使用,js两种方式定义正则的写法。
javascript
var a = '2012-12-13';
//注意:这里都是双斜杠,前后不需要/包裹
var reg = new RegExp("(\\d{4})-(\\d{2})-(\\d{2})","gi");
var m1 = a.replace(reg,"$1年\n$2月$3日");
//注意:这里都是单斜杠,前后需要/包裹
var m2 = a.replace(/(\d{4})-(\d{2})-(\d{2})/g,"$1年\n$2月$3日");
//PS:两个结果一致,结果是替换为年月日
JS 在线测试
正则:(\d{4})-(\d{2})-(\d{2})
文本:2012-12-13
替换:$1年$2月$3日
输出:2012年12月13日
参考:正则表达式在线测试。
补充:修饰符的用法
g: 全局匹配,匹配到一个不结束,正则默认找到一个就结束
i: 忽略大小写搜索,正则默认有区分大小写
m: 多行搜索,此时^和 锚字符也可以代表行头和尾,例如 / j a v a 锚字符也可以代表行头和尾,例如/java 锚字符也可以代表行头和尾,例如/java/im也可以匹配"JAVA\nJapan"
gim: 以上组合,顺序可以随意
修饰符在两种写法里面的位置如下:
var pattern = /\w / i g m ; v a r p a t t e r n = n e w R e g E x p ( " w /igm; var pattern = new RegExp("\\w /igm;varpattern=newRegExp("w","igm");
补充:正则表达式概念
正则表达式是一个描述字符模式的对象,JS的RegExp类表示正则表达式,String和RegExp都定义了使用正则表达式的方法。
创建RegExp对象,可以使用RegExp构造函数或者使用特殊的直接量。
如下面两种方式等价:
javascript
var pattern = /\w$/;
//第二种写法里面若用到转义字符,反斜杠必须写2个。
var pattern = new RegExp("\\w$");
Tips:字母和数字匹配本身,\n是换行符,从此之外,许多标点符号具有特殊含义。
JS - String 的正则用法
JS String 类型支持4种使用正则表达式的方法。
【search】- 查找
语法:string.search(searchvalue)
参数:必须,查找的字符串或者正则表达式。
语义:search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。
补充:如果没有找到任何匹配的子串,则返回 -1。
javascript
var pattern = /b\w+/;
var tempStr = 'abaa';
alert(tempStr.search(pattern)); //弹出1
var str="Visit Runoob!";
str.search("Runoob"); //值为6
注1:若参数不是正则,则会用RegExp的构造函数去构造它。
注2:search方法不支持全局检索g。
注3:这个方法用的较少。
【replace】- 替换
语法:string.replace(searchvalue,newvalue)
参数 searchvalue:规定子字符串或要替换的模式的 RegExp 对象,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象。
参数 newvalue:一个字符串值,规定了替换文本或生成替换文本的函数(即一个有返回值的函数)。
补充:注意事项
1、该方法并不会改变原字符串的值,但是可以返回值赋值给另外一个字符串;
2、参数1若是字符串而不是正则,将直接搜索这个字符串,而不像search那样转换成正则;
3、用修饰符g,也可以实现全局替换,类似 replaceAll 的效果;
4、参数2可以使用更复杂的操作,$加数字,代表之前圆括号的字表达式匹配的文本。
补充:replace 与 replaceAll 方法的区别
两者的参数和用法基本一致,都支持字符串和正则替换,但 replace 用在字符串的时候,默认就替换第一个,而后者是全部;但 replace 可以用 g 的正则模式实现和 replaceAll 一样的效果。
javascript
var str="Visit Microsoft! Visit Microsoft!";
str.replace("Microsoft","Runoob"); //'Visit Runoob! Visit Microsoft!'
str.replaceAll("Microsoft","Runoob"); //'Visit Runoob! Visit Runoob!'
str.replace(/Microsoft/g,"Runoob"); //'Visit Runoob! Visit Runoob!'
Tips:若 replaceAll 第一个参数是正则,那么必须添加修饰符g,否则提示:
String.prototype.replaceAll called with a non-global RegExp argument
补充:若干范例
javascript
//例1:
var reg=new RegExp("\\d{2}-\\d{5}","gi"); --第一步,创建正则表达式规则,验证由两位数字、一个连字符再加 5 位数字组成的 ID 号。
var stringObj1 = "22-33333"; --第二步,定义需要进行替换的字符串
stringObj1=stringObj1.replace(reg,"4"); --第三步,执行替换,并返回结果重新赋值给自己或新变量
alert("22-33333".replace(/\d{2}-\d{5}/g, 4)); --简写版本
//例2:
var reg=new RegExp("b(\\w+)","gi"); //创建正则RegExp对象
var stringObj="abcC";
var newstr=stringObj.replace(reg,"am$1"); //弹出aamcC
//例3:
var tempStr = '"abaa"';
alert(tempStr.replace(/"([^"]*)"/g,'[$1]')) -- 弹出[abaa]
//例4:名字中间替换*,输出"朽**哉"
var reg=/^(.{1})(.*)(.{1})$/;
var str="朽木白哉";
str=str.replace(reg,function(a,b,c,d){
return b+c.replace(/./g,"*")+d;
});
【match】- 匹配
语义:返回一个由匹配结果组成的数组,匹配不到返回null,参数和search方法一样。
注意:是否设置全局变量,关系到返回值。
1、若设置了全局修饰符g,则返回数组包含的是所有匹配结果;
2、若没有设置g,那返回的也是一个数组a,只有a[0]是匹配结果,a[1]到a[n]装的是 1 − 1- 1−n的内容。
javascript
str="The rain in SPAIN stays mainly in the plain";
str.match(/ain/gi);
//(4) ['ain', 'AIN', 'ain', 'ain']
str.match(/ain/);
//不带类数组对象,不是真正的数组
//['ain', index: 5, input: 'The rain in SPAIN stays mainly in the plain', groups: undefined]
str.match(/ain(\w{1})/)
//['ainl', 'l', index: 25, input: 'The rain in SPAIN stays mainly in the plain', groups: undefined]
//数组0是匹配对象,1是括号里面内容,上面的l
//在很大程度上有赖于 regexp 是否具有标志 g。
//如果 regexp 没有标志 g,那么 match() 方法就只能在 stringObject 中执行一次匹配。
//如果没有找到任何匹配的文本, match() 将返回 null。
//否则它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
var str = "my name is zhangshan,ZHANGSHAN is very strong.";
var objRegExp = /zhang/i;
var n = str.match(objRegExp);
console.log(typeof n); // 输出:object
console.log(n); // 输出:["zhang", index: 11, input: "my name is zhangshan,ZHANGSHAN is very strong.", groups: undefined]
var objRegExp = /zhang/ig;
var n = str.match(objRegExp);
console.log(typeof n); // 输出:object
console.log(n); // 输出:["zhang", "ZHANG"]
【split】- 拆分
语义:用于把一个字符串分割成字符串数组,参数也可以是正则表达式。
语法:string.split(separator,limit)
参数1:可选,字符串或正则表达式,从该参数指定的地方分割 string Object
参数2:可选,该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
提示: 如果把空字符串 ("") 用作 separator,那么 stringObject 中的每个字符之间都会被分割。
注意: split() 方法不改变原始字符串。
javascript
//使用 limit 参数,n 将输出3个数组的值,How,are,you
var str="How are you doing today?";
var n=str.split(" ",3);
//正则拆分范例
var str = "my name is zhangshan,ZHANGSHAN IS very nice.";
var objRegExp = /zhang/i;
var n = str.split(objRegExp);
console.log(typeof n); // 输出:object
console.log(n); // 输出:["my name is ", "shan,", "SHAN is very nice."]
var objRegExp = (/zhang|is/i);
var n = str.split(objRegExp);
console.log(typeof n); // 输出:object
console.log(n); // 输出:["my name ", " ", "shan,", "SHAN ", " very nice."]
JS - RegExp 的正则用法
Tips:RegExp 对象支持三个方法和一些属性。
【构造函数RegExp()】
语义:构造一个正则表达式RegExp模式,参数可以有2个,第一个参数是两个/里面的内容(字符串形式),第二个是可选的igm组合。
例如:var pattern = new RegExp("\w","igm");
注意:里面的 \ 都要写成2个 \,也可以使用直接量的方式定义。
【正则表达式修饰符】
i:表示匹配的时候忽略大小写;
g:表示全文查找,全局匹配,匹配到一个不结束;
m:表示多行查找;
gim: 以上组合
【补充:多行修饰符】
多行修饰符 m 常用于执行一个多行匹配。像之前介绍的 (^, ) 用于检查格式是否是在待检测字符串的开头或结尾。但我们如果想要它在每行的开头和结尾生效,我们需要用到多行修饰符 m 。例如,表达式 / a t ( . ) ? ) 用于检查格式是否是在待检测字符串的开头或结尾。但我们如果想要它在每行的开头和结尾生效,我们需要用到多行修饰符 m。 例如,表达式 /at(.)? )用于检查格式是否是在待检测字符串的开头或结尾。但我们如果想要它在每行的开头和结尾生效,我们需要用到多行修饰符m。例如,表达式/at(.)?/gm 表示小写字符 a 后跟小写字符 t ,末尾可选除换行符外任意字符。根据 m 修饰符,现在表达式匹配每行的结尾。
latex
//匹配最后一行的mat
"/.at(.)?$/" => The fat
cat sat
on the mat.
//匹配每一行的at
"/.at(.)?$/gm" => The fat
cat sat
on the mat.
【RegExp的五个属性】
source:代表正则表达式文本(只读)
global:代表是否有g(只读)
ignoreCase:代表是否有i(只读)
multiline:代表是否有m(只读)
lastIndex:是可读/可写的整数,如果匹配带有g的话,此属性存储下次检索的开始位置,在exec和text方法中会用到。
【exec】
语义:与match作用相同,只不过是用正则去调用,然后字符串作为参数。
若没有设置g,那返回的也是一个数组,和match相同,只找一次;
若设置了全局修饰符g,则它可以多次调用,每次执行的时候,匹配一次,在这个过程中,lastIndex一直在变化,一直执行会一直得到结果,直到找不到
javascript
var str = "my name is zhangshan,ZHANGSHAN is very strong.";
var objRegExp = /zhangshan/g;
var q = objRegExp.exec(str);
if (typeof q === "object") {
console.log(typeof q); // 输出:object
console.log(q.length); // 输出:1
console.log(q); // 输出:["zhangshan", index: 11, input: "my name is zhangshan,zhangshan is very strong.", groups: undefined]
console.log(q.index); // 输出:11
var first = q[0];
console.log(typeof first); // 输出:string
console.log(first); // 输出:zhangshan
}
javascript
var str = "my name is zhangshan,ZHANGSHAN is very strong.";
var objRegExp = /zhangshan/gi;
objRegExp.exec(str)
//['zhangshan', index: 11, input: 'my name is zhangshan,ZHANGSHAN is very strong.', groups: undefined]
objRegExp.exec(str)
//['ZHANGSHAN', index: 21, input: 'my name is zhangshan,ZHANGSHAN is very strong.', groups: undefined]
objRegExp.exec(str)
//null
【test】
javascript
var reg = /^[0-9a-zA-Z_\-]+$/; //横杠是特殊字符,要用反斜扛将它转义:[\-]
if(!reg.test(userCode)){
alert("你输入的字符组合必须是数字,字母,下划线'_',横杠'-'")
return;
}
var objRegExp = /zhangshan/g;
var p = objRegExp.test(str);
console.log(typeof p); // 输出:boolean
console.log(p); // 输出:true
【关于lastIndex】
JS中正则表达式的使用方式有两种,一种是正则表达式对象的方法,一种是字符串对象的方法,前者有exec(str)、test(str)两个方法,后者有match(regexp)、replace(regexp)、search(regexp)、split(search)四个方法。当作为正则表达式对象的方法使用时,要特别注意它的lastIndex属性。
var regexp = /abcd/g;
var str = 'abcdefg';
alert(regexp.test(str)); //true
alert(regexp.test(str)); //false
alert(regexp.test(str)); //true
上面这段代码运行的结果分别是弹出true、false、true,考虑到用的是同一个正则模式,是不是让人有点迷糊?
其实这正是正则表达式对象的lastIndex属性在作怪。lastIndex从字面上来讲就是最后一个索引,实际上它的意思是正则表达式开始下一次查找的索引位置,第一次的时候总是为0的,第一次查找完了的时候会把lastIndex的值设为匹配到得字符串的最后一个字符的索引位置加1,第二次查找的时候会从lastIndex这个位置开始,后面的以此类推。如果没有找到,则会把lastIndex重置为0。要注意的是,lastIndex属性只有在有全局标志正则表达式中才有作用,如果我们把上面代码中正则表达式的g标志去掉,那么三次弹出的就都是true了。
exec()方法同样是如此,exec()方法返回的是一个数组,数组的第一个元素是匹配到的字符串,之后的元素则分别对应匹配到的字串,也就是正则表达式中用括号括起来的那些。如果使用exec()方法的正则表达式没有全局标志,则只会匹配第一个,如果正则表达式有全局标志,则可以循环使用exec()来得到所有的匹配,直到exec()返回null为止,也就是找不到匹配了。这里能够循环使用同一个正则表达式的exec()方法,靠的就是lastIndex,因为带全局标志的正则表达式每次匹配后都会更新lastIndex的值作为下次查找匹配的起点。
最后要说明的是字符串的正则方法里lastIndex属性是不起作用的,不管正则模式是不是全局的。
最常用的 test exec match search replace split 6个方法
PS:包含4个String类型的方法
javascript
1) test 检查指定的字符串是否存在
var data = "123123″;
var reCat = /123/gi;
alert(reCat.test(data)); //true
//检查字符是否存在 g 继续往下走 i 不区分大小写
2) exec 返回查询值
var data = "123123,213,12312,312,3,Cat,cat,dsfsdfs,";
var reCat = /cat/i;
alert(reCat.exec(data)); //Cat
3)match 得到查询数组
var data = "123123,213,12312,312,3,Cat,cat,dsfsdfs,";
var reCat = /cat/gi;
var arrMactches = data.match(reCat)
for (var i=0;i < arrMactches.length ; i++)
{
alert(arrMactches[i]); //Cat cat
}
4) search 返回搜索位置 类似于indexof
var data = "123123,213,12312,312,3,Cat,cat,dsfsdfs,";
var reCat = /cat/gi;
alert(data.search(reCat)); //23
5) replace 替换字符 利用正则替换
var data = "123123,213,12312,312,3,Cat,cat,dsfsdfs,";
var reCat = /cat/gi;
alert(data.replace(reCat,"libinqq"));
6)split 利用正则分割数组
var data = "123123,213,12312,312,3,Cat,cat,dsfsdfs,";
var reCat = /\,/;
var arrdata = data.split(reCat);
for (var i = 0; i < arrdata.length; i++)
{ alert(arrdata[i]);}
URL参数获取相关的操作
javascript
//下面的例子用来获取url的两个参数,并返回urlRewrite之前的真实Url
var reg=new RegExp("(http://www.qidian.com/BookReader/)(\\d+),(\\d+).aspx","gmi");
var url="http://www.qidian.com/BookReader/1017141,20361055.aspx";
//方式一,最简单常用的方式
var rep=url.replace(reg,"$1ShowBook.aspx?bookId=$2&chapterId=$3");
alert(rep);
//方式二 ,采用固定参数的回调函数
var rep2=url.replace(reg,function(m,p1,p2,p3){return p1+"ShowBook.aspx?bookId="+p3+"&chapterId="+p3});
alert(rep2); --m是url的值,p1指代上面的$1
//方式三,采用非固定参数的回调函数
var rep3=url.replace(reg,function(){var args=arguments; return args[1]+"ShowBook.aspx?bookId="+args[2]+"&chapterId="+args[3];});
alert(rep3); --同上,就是利用了arguments
//方式四和方法三很类似, 除了返回替换后的字符串外,还可以单独获取参数
var bookId;
var chapterId;
function capText()
{
var args=arguments;
bookId=args[2];
chapterId=args[3];
return args[1]+"ShowBook.aspx?bookId="+args[2]+"&chapterId="+args[3];
}
var rep4=url.replace(reg,capText);
alert(rep4);
alert(bookId);
alert(chapterId);
//使用test方法获取分组
var reg3=new RegExp("(http://www.qidian.com/BookReader/)(\\d+),(\\d+).aspx","gmi");
reg3.test("http://www.qidian.com/BookReader/1017141,20361055.aspx");
//获取三个分组
alert(RegExp.$1);
alert(RegExp.$2);
alert(RegExp.$3);
相关机理补充
var reg=new RegExp("\d","g");
var str="abd1afa4sdf";
str.replace(reg,function(){alert(arguments.length);});
匿名函数竟然被执行了二次,并且在函数里还带有三个参数,为什么会执行二次呢??
这个很容易想到,因为我们写的正则表达式是匹配单个数字的,而被检测的字符串刚好也有二个数字,
故匿名函数被执行了二次。在匿名函数内部的那三个参数到底是什么内容呢??
为了弄清这个问题,我们看下面的代码。
经过观察我们发现,第一个参数表示匹配到的字符,
第二个参数表示匹配时的字符最小索引位置(RegExp.index),
第三个参数表示被匹配的字符串(RegExp.input)。
其实这些参数的个数,还会随着子匹配的变多而变多的。
特别注意:当正则表达式里面含有()的话,会加第一个参数后面加入 1 到 1到 1到n等参数,原第二第三参数顺延,可以参考下面补充说明的方式二 ,采用固定参数的回调函数。因此,在面对很复杂的正则和逻辑的时候,可以采用这种方式去调试或处理指定参数值。
Java 正则用法
运用简介
【技术简介】
Java 正则表达式通过 java.util.regex 包下的 Pattern 类与 Matcher 类实现,他们的构造方法都是私有的,不能 new 实例化。Pattern 类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将 Pattern 与 Matcher 一起合作。Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持。
英文:regular expression => RegExp
【java.util.regex】
java.util.regex 包主要包括以下三个类:
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
典型运用
【典型运用 - 匹配搜索】
正则表达式一般用在针对文本的搜索、替换、格式校验等场景,并且处理效率较高。
Java 正则表达式的搜索代码如下:
java
// Step0、需要处理的文本
String content = "1998 年 12 月 8 日,第二代 Java 平台的企业版 J2EE 发布。1999 年 6 月,Sun 公司发布了" +
"第二代 Java 平台(简称为 Java2)的 3 个版本";
// Step1、定义正则字符串
String regStr="(\\d\\d)(\\d\\d)";
// Step2、创建模式对象,即正则表达式对象
Pattern pattern = Pattern.compile(regStr);
// Step3、创建匹配器,按照正则表达式的规则去匹配content字符串
Matcher matcher = pattern.matcher(content);
// Step4、开始查找符合要求的内容并输出
while (matcher.find()){
System.out.println("找到->"+matcher.group(0));
System.out.println("第一组找到的值->"+matcher.group(1));
System.out.println("第二组找到的值->"+matcher.group(2));
}
【典型运用 - 忽略大小写】
Java 正则,默认是不忽略大小写,可以通过设置 Pattern.CASE_INSENSITIVE 让其忽略。
PS:值得一提的是,Java 正则没有全局模式g,find 方法是类似JS正则的非全局模式的,matches 则为全局模式。
java
// Step0、需要处理的文本
String content = "j123456";
// Step1、定义正则字符串,不以字母J开头
String regStr = "^(?!J).*";
// Step2、创建模式对象,即正则表达式对象
// 没加该参数输出满足,加上忽略后是输出不满足
Pattern pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);
// Step3、创建匹配器,按照正则表达式的规则去匹配content字符串
Matcher matcher = pattern.matcher(content);
if (matcher.matches()) {
System.out.println("满足");
} else {
System.out.println("不满足");
}
【典型运用 - 匹配校验】
java
String test = "2012-12-12 08:00:00";
String regStr = "\\d{4}-\\d{2}-\\d{2}";
// 完全匹配,输出false
System.out.printf(Pattern.matches(regStr, test) + "");
// 部分匹配,输出true
Pattern p = Pattern.compile(regStr);
Matcher m = p.matcher(test);
System.out.printf(m.lookingAt() + "");
【典型运用 - 正式项目】
java
//定义为常量,方便复用,否则IDEA会提示
private final static String REG = "@(.*?)@";
private final static Pattern SQL_PATTERN
= Pattern.compile(REG, Pattern.CASE_INSENSITIVE);
//查找所有@xxx@类型的内容,并取其中的xxx
Matcher m = SQL_PATTERN.matcher(sql);
底层原理
以搜索匹配 matcher.find 代码为前提,分析一下源码:
java
// 题目:经典示例代码,找到四个连续数字,可以找到1998、1999、3443
// Step0、需要处理的文本
String content = "1998 年 12 月 8 日,第二代 Java 平台的企业版 J2EE 发布。1999 年 6 月,Sun 公司发布了,第二代 Java 平台(简称为 Java2)的 3 个版本,我叫3443";
// Step1、定义正则字符串
String regStr = "(\\d\\d)(\\d\\d)";
// Step2、创建模式对象,即正则表达式对象
Pattern pattern = Pattern.compile(regStr);
// Step3、创建匹配器,按照正则表达式的规则去匹配content字符串
Matcher matcher = pattern.matcher(content);
// Step4、开始查找符合要求的内容并输出
while (matcher.find()) {
System.out.println("匹配正则的结果->" + matcher.group(0));
System.out.println("第一组->" + matcher.group(1));
System.out.println("第二组->" + matcher.group(2));
}
//1、matcher.find() 每次执行完,就会修改 matcher 对象的groups[]属性
//2、匹配到的子文本,其开始索引和结束索引分别记录在groups的0和1位置,同时把下次开始检索的位置存入oldLast属性
//3、如果存在一个()分组,则插入groups的[2]-[3]位置,如果有多个分组,则以此类推
//4、find 和 group 的逻辑就是根据参数从 group 数组提取字符串索引,然后利用字符串的subString处理
//5、下次再执行find,就继续更新相关属性值,上一段源码
public String group(int group) {
if (first < 0)
throw new IllegalStateException("No match found");
if (group < 0 || group > groupCount())
throw new IndexOutOfBoundsException("No group " + group);
if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
return null;
return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
}
操作说明
【Pattern -- 简单正则】
PS:用的较少,一般试用Matcher
java
//p的规则在创建的时候就指定
//注意,compile方法的参数是用双引号包裹的字符串
//由于Java字符串特性,反斜杠必须转义,这和JS定义正则不同。
Pattern p=Pattern.compile("\\w+");
//返回regex正则参数,这里是 \w+
p.pattern();
//实际案例
Pattern p=Pattern.compile("\\d+");
String[] str=p.split("我的QQ是:456456我的电话是:0532214我的邮箱是:aaa@aaa.com");
//结果:str[0]="我的QQ是:" str[1]="我的电话是:" str[2]="我的邮箱是:aaa@aaa.com"
//用于分隔字符串,并返回一个String[]
Pattern.split(CharSequence input)
//Pattern.matcher(String regex,CharSequence input)是一个静态方法
//用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串.
//代码示例:
Pattern.matches("\\d+","2223");//返回true
Pattern.matches("\\d+","2223aa");//返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到
Pattern.matches("\\d+","22bb23");//返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到
//Pattern.matcher(CharSequence input) 返回一个Matcher对象.
//Matcher类的构造方法也是私有的,不能随意创建,
//只能通过Pattern.matcher(CharSequence input)方法得到该类的实例.
//代码示例:
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.pattern();//返回p 也就是返回该Matcher对象是由哪个Pattern对象的创建的
【Matcher -- 复杂正则】
Matcher 类提供三个匹配操作方法,三个方法均返回boolean类型,当匹配到时返回true,没匹配到则返回false
1、matches():对整个字符串进行匹配,只有整个字符串都匹配了才返回true,和Pattern的matches方法相同
2、lookingAt():对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true
3、find():对字符串进行匹配,匹配到的字符串可以在任何位置
PS:每调用一次find方法,进行匹配一次,指针也向后,可以使用while循环判断输出
matches()
对整个字符串进行匹配,只有整个字符串都匹配了才返回true
Java代码示例:
Pattern p=Pattern.compile("\d+");
Matcher m=p.matcher("22bb23");
m.matches();//返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功.
Matcher m2=p.matcher("2223");
m2.matches();//返回true,因为\d+匹配到了整个字符串
我们现在回头看一下Pattern.matcher(String regex,CharSequence input),它与下面这段代码等价
Pattern.compile(regex).matcher(input).matches()
**Mathcer.start()/ Matcher.end()/ Matcher.group() **
当使用matches()、lookingAt()、find()执行匹配操作后,就可以利用以上三个方法得到更详细的信息.
start():返回匹配到的子字符串在字符串中的索引位置.
end():返回匹配到的子字符串的最后一个字符在字符串中的索引位置.
group():返回匹配到的子字符串
补充:
1、这三个参数都可以加上整数参数(组号-小括号的个数),并搭配groupCount方法使用
2、这三个方法必须在使用find等方法匹配后才能使用,否则会抛出java.lang.IllegalStateException
3、find等并不是单纯判断是否匹配,而是进行匹配并获取相关信息,同jdbc的next方法
Matcher.replaceAll(String replacement) / Matcher.replaceFirst(String replacement)
和String的这两个方法差不多,使用m.replaceAll("*")方式替换
【具体示例】
java
//Matcher 的基础运用,默认贪婪匹配
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("aaa2223bb");
m.find();//true
m.start();//返回3
m.end();//返回7,返回的是2223后的索引号
m.group();//返回2223
//每次找到内容就输出
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("aaa2223bb2224");
while(m.find()){
System.out.println(m.group());
}
//获取小括号内容1
String a = "ftfrft#{select * from xx}hjuju#{select * from cc}aaaaa";
Pattern p=Pattern.compile("([#{]{2})(.*?)}+");
Matcher m=p.matcher(a);
while(m.find()){
System.out.println(m.group(1));
System.out.println(m.group(2));
}
输出结果:
#{
select * from xx
#{
select * from cc
//获取小括号内容2
public static void main(String[] args) {
String a = "xxxxx#{sssss}";
Pattern p=Pattern.compile("[#{]{2}(.*?)}+");
Matcher m=p.matcher(a);
while(m.find()){
System.out.println(m.group(1));
}
}
输出结果:sssss
字符串运用
【字符串之替换正则】
reportXml = reportXml.replaceAll("LAB_REPORT_DETAIL\d*", "LAB_REPORT_DETAIL").replace("null", "");
Tips:注意返回值要赋值,不然无效。
【replace、replaceAll、replaceFirst的区别详解】
【问题实例】
String s = "my.test.txt";
System.out.println(s.replace(".", "#")); //my#test#txt
System.out.println(s.replaceAll(".", "#")); //###########
System.out.println(s.replaceFirst(".", "#")); //#y.test.txt
【问题剖析】
String的replaceAll跟replaceFirst用到了正则表达式,"."是正则表达式的元字符,匹配除换行符以外的任意字符,所以replaceAll、replaceFirst才出现了这样的结果。
而replace没有用到正则表达式,但会把所有"."替换掉,很多人可能会误解replace是替换单个,而replaceAll是替换全部,其实这是错的。replace只是没有用到正则表达式,但会替换所有匹配的字符串。
【扩展说明】
到这里一些不懂正则表达式的小伙伴可能就要喊坑爹了,"那我不想用正则表达式去替换第一个字符串肿么办?"其实也很简单,只要将元字符串转义就行了。
s.replaceFirst("\.", "#"); //运行结果:my#test.txt
Oracle 正则用法
技术简介
Tips:Oracle 也支持正则表达式,基本用法都是一样的,可能少数功能不支持。
Tips:不用特别学习 Oracle 的表达式,场景没有很多,主要是模糊搜索。
支持正则表达式的函数主要有四个 :
1,REGEXP_LIKE :与LIKE的功能相似
2,REGEXP_INSTR :与INSTR的功能相似
3,REGEXP_SUBSTR :与SUBSTR的功能相似
4,REGEXP_REPLACE :与REPLACE的功能相似
它们在用法上与Oracle SQL 函数LIKE、INSTR、SUBSTR 和REPLACE 用法相同,
但是它们使用POSIX 正则表达式代替了老的百分号(%)和通配符(_)字符。
Tips:很明显,REGEXP_LIKE 的利用较多。
常用符号
POSIX 正则表达式由标准的元字符(metacharacters)所构成:
'^' 匹配输入字符串的开始位置,在方括号表达式中使用,此时它表示不接受该字符集合。
'$' 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。
'.' 匹配除换行符之外的任何单字符。
'?' 匹配前面的子表达式零次或一次。
'+' 匹配前面的子表达式一次或多次。
'*' 匹配前面的子表达式零次或多次。
'|' 指明两项之间的一个选择。例子'^([a-z]+|[0-9]+)$'表示所有小写字母或数字组合成的字符串。
'( )' 标记一个子表达式的开始和结束位置。
'[]' 标记一个中括号表达式。
'{m,n}' 一个精确地出现次数范围,m=<出现次数<=n,'{m}'表示出现m次,'{m,}'表示至少出现m次。
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。
字符簇:
[[:alphanum:]]匹配任何字母和数字。
[[:alpha:]] 任何字母。
[[:digit:]] 任何数字。
[[:alnum:]] 任何字母和数字。
[[:space:]] 任何白字符。
[[:upper:]] 任何大写字母。
[[:lower:]] 任何小写字母。
[[:punct:]] 任何标点符号。
[[:xdigit:]] 任何16进制的数字,相当于[0-9a-fA-F]。
常用写法
sql
-- 搜索字符特定格式
select * from zoeportal.por_user_login_config t
where regexp_like(t.photo_address, '/portal-web.*?\.jpg');
--返回以数值或字母开头的结果
select * from v where regexp_like(data, '^[0-9A-Za-z]');
--返回以数值或字母结尾的结果
select * from v where regexp_like(data, '[0-9A-Za-z]$');
--返回只包含数值和字母的结果
--注意,这里必须有加号 + ,加号表示至少匹配一次或多次,* 星号表示至少匹配0次或多次;
--如果不加,就是只匹配一次
select * from v where regexp_like(data, '^[0-9A-Za-z]+$');
--搜索不以6开头的
select a.inp_case_no
from zoepatient.pat_admission_record a
where 1 = 1
and not regexp_like(a.inp_case_no, '^6.*')
--查询value中以1开头60结束的记录并且长度大于5位位
select * from fzq where regexp_like(value,'^1.{2,}60$');
--查询value中以1开头60结束的记录并且长度是7位并且全部是数字的记录。
--使用like就不是很好实现了。
select * from fzq where regexp_like(value,'^1[0-9]{4}60$');
-- 也可以这样实现,使用字符集。
select * from fzq where regexp_like(value,'1[[:digit:]]{4}60');
-- 查询value中不是纯数字的记录
select * from fzq where not regexp_like(value,'^[[:digit:]]+$');
-- 查询value中不包含任何数字的记录。
select * from fzq where regexp_like(value,'^[^[:digit:]]+$');
--查询以12或者1b开头的记录.不区分大小写。
select * from fzq where regexp_like(value,'^1[2b]','i');
--查询以12或者1b开头的记录.区分大小写。
select * from fzq where regexp_like(value,'^1[2B]');
-- 查询数据中包含空白的记录。
select * from fzq where regexp_like(value,'[[:space:]]');
--查询所有包含小写字母或者数字的记录。
select * from fzq where regexp_like(value,'^([a-z]+|[0-9]+)$');
--查询任何包含标点符号的记录。
select * from fzq where regexp_like(value,'[[:punct:]]');
总结陈词
上文简单介绍了正则表达式在不同语言下的基础使用,正则表达式还有诸多用法,由于篇幅受限,将放在后续篇章介绍。
日常开发中,正则虽然只是一个小技术点,但掌握了正则表达式,可以说对字符串的处理将得心应手。
喊一下口号:"正则在手,天下我有"。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。