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

📢 大家好,我是 【战神刘玉栋】,有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:]]');

总结陈词

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

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

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

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

相关推荐
夜泉_ly32 分钟前
MySQL -安装与初识
数据库·mysql
qq_529835352 小时前
对计算机中缓存的理解和使用Redis作为缓存
数据库·redis·缓存
月光水岸New4 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6754 小时前
数据库基础1
数据库
我爱松子鱼4 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo4 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser5 小时前
【SQL】多表查询案例
数据库·sql
Galeoto5 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
人间打气筒(Ada)6 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231116 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql