《字符串杀手锏 · 正则表达式之一》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍

文章目录

写在前面的话

正则表达式,英文全称 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:]]');

总结陈词

上文简单介绍了正则表达式在不同语言下的基础使用,正则表达式还有诸多用法,由于篇幅受限,将放在后续篇章介绍。

日常开发中,正则虽然只是一个小技术点,但掌握了正则表达式,可以说对字符串的处理将得心应手。

喊一下口号:"正则在手,天下我有"。

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

相关推荐
丁总学Java24 分钟前
MySQL高级-MVCC-原理分析(RR级别)
数据库·mysql·mvcc·rr级别·可重复读
“抚琴”的人24 分钟前
C#——Path类详情
开发语言·数据库·microsoft·c#
创作小达人25 分钟前
InnoDB中的表级锁、页级锁、行级锁详解
java·数据库·mysql
周杰伦的稻香1 小时前
“LNMP环境搭建实战指南:从零开始配置CentOS 7下的Nginx、MySQL与PHP“
mysql·nginx·centos
missterzy1 小时前
Spring Boot 创建定时任务
java·数据库·spring boot·定时任务
m0_748171582 小时前
数据库第一次作业
数据库
V+zmm101342 小时前
springcloud分布式架构网上商城 LW +PPT+源码+讲解
java·数据库·后端·vue·idea·springclud
苏生Susheng2 小时前
【Oracle】Oracle常用语句大全
java·数据库·sql·mysql·oracle·sql语句·数据库语法
叶孤程2 小时前
【Qt知识】window frame 对窗口坐标的影响
数据库·qt·microsoft
dot.Net安全矩阵3 小时前
.NET 漏洞分析 | 某ERP系统存在SQL注入
数据库·sql·安全·web安全·矩阵·.net