欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

正则表达式】正则表达式简单摘要

最编程 2024-07-16 21:31:27
...

正则表达式

1.介绍

平时会用到一些正则表达式,抽时间把一些知识点整理了下,方便自己和他人查看使用。

1.1 简介

正则表达式是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。它描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。 由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义。

比如/ab?(c|de*)+|fg/

image-20220211145232066

1.上面由于括号的存在,那么(c|de*)是一个整体,而括号里又由于存在分支结构,那么cde*又是一个整体,再往细分,de\*又是一个整体

2.整个语句拆分下来就是:

a、b?、(...)+、f、g 其中括号里的为 c、de*

3.上面的意思根据结果可以分解为 ab?(...)+ 或 fg

​ 3.1 左边:a必须要有一个;b可以不存在,也可以存在,存在时只能有一个b;c或者de*至少存在一个,其中e可以不存在,或者可以存在多个

​ 3.2右边:fg都要存在

测试结果:

image-20220211150542420

1.2 在线工具

1.regulex

image-20220211141613890

2.regexper

image-20220211141647836

3.在线验证工具

image-20220212160926525

4.regexr

image-20220212160948542

离线工具:RegexBuddy

2.相关概念

2.1 普通字符

普通字符包括没有显式指定为元字符的所有可打印不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

可打印的:

比如 123456abcdefGHIJKLM

不可打印的(特殊字符,这种特殊字符的含义是打印效果来说的):

特殊字符 正则表达式 记忆方式
换行符 \n new line
换页符 \f form feed
回车符 \r return
空白符 \s space
制表符 \t tab
垂直制表符 \v vertical tab
回退符 [\b] backspace,之所以使用[]符号是避免和\b重复
var regex = /ab/g;
var string = "abc 12abv acbdd";
console.log( string.match(regex) ); 
//通过匹配,会把ab这两个单词的组合匹配并查询出来
[ 'ab', 'ab' ]

2.2 元字符

元字符有一个预先定义的含义,使某些共同的pattern更易于使用,例如 \d 代替 [0…9]。元字符就是含有正则表达式含义的一种特殊字符。

常用元字符如下

字符 描述
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 。? 等价于 {0,1}。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
. 匹配除换行符(\n、\r)之外的任何单个字符,相等于 [^\n\r]。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\w 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'。
\W 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]'。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
... ...

2.3 定位符

定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。

定位符用来描述字符串或单词的边界,^$ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。

字符 描述
^ 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。
\b 单词边界,即是\W\w以外的任何一个字符,规定匹配字符必须出现在目标字符串的开头或者结尾两个边界之一
\B 规定匹配字符必须出现在目标字符串的开头和结尾两个边界之内

var regex=/^an/g;
var string="an This is an\nan tzone good an"; 
console.log(string.match(regex));
//结果:虽然这个字符串有换行符,但是正则此时还是认为该文本整体就是一个字符串,所以只有一个an匹配出来
[ 'an' ] 

var regex=/^an/gm;
var string="an This is an\n an tzone good an"; 
console.log(string.match(regex));
//结果:虽然指定了多行文本模式了,但是\n an中间多了一个空格,那么换行后,第二个an其实并不是该行的首字符,所以也匹配不上
[ 'an' ] 

var regex=/^an/gm;
var string="an This is an\nan tzone good an"; 
console.log(string.match(regex));
//结果:
[ 'an','an' ] 

var regex=/ab\b/g; //表示ab这两个字符必须出现在单词字符串的末尾才行
var string="wwabc ab2 imab 55ab"; 
console.log(string.match(regex));
//结果:
[ 'ab', 'ab' ]

var regex=/\bab/g; //表示ab这两个字符必须出现在单词字符串的前面才行
var string="wwabc ab2 imab 55ab"; 
console.log(string.match(regex));
//结果:
[ 'ab' ]

var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#');
console.log(result); 
//结果:
[#JS#] #Lesson_01#.#mp4#


2.4 限定符(量词)

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 ***** 或 +?{n}{n,}{n,m} 共6种。

字符 描述
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 、 "does" 中的 "does" 、 "doxy" 中的 "do" 。? 等价于 {0,1}
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。

注意:不能将限定符与定位符一起使用。由于在紧靠换行或者单词边界的前面或后面不能有一个以上位置,因此不允许诸如 ^* 之类的表达式。

2.4.1 匹配原则

匹配模式默认是贪婪模式,即匹配越多越好

匹配模式要指定非贪婪模式,那么可以在量词后面再加上一个?即可

2.5 范围符

正则提供一个元字符中括号 [] 来表示区间条件

1.限定某些数字,比如[162],那么匹配到的就可以是其中的某个数字,比如

var regex = /[162]/g;
var string = "123 768 8862";
console.log( string.match(regex) ); 
//结果如下:可以看出,只要符合中括号里的数字,都会匹配出来
[ '1', '2', '6', '6', '2' ]

[]里的字符的关系默认是 的关系

2.用-可以增加一个范围,比如[1-5A-C]

var regex = /[1-5A-C]/g;
var string = "13 27 MMAK OCBAZ";
console.log( string.match(regex) ); 
//结果如下:
[
  '1', '3', '2',
  'A', 'C', 'B',
  'A'
]

3.正则表达式的特殊符号,被包含到中括号中,则失去特殊意义,除了^-

^:放在中括号里表示取反的意思 
var regex = /[^6]/ig;
var string = "23456 567";
console.log( string.match(regex) );
//结果如下:
[
  '2', '3', '4',
  '5', ' ', '5',
  '7'
]

2.6 修饰符

可以根据修饰符来指定额外的匹配策略

修饰符 含义 描述
i ignore - 不区分大小写 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。
g global - 全局匹配 查找所有的匹配项。
m multi line - 多行匹配 使边界字符 ^$ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。
s 单行模式,特殊字符圆点 . 中包含换行符 \n 默认情况下的圆点 . 是 匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。

举例如下:

//当没有修饰符时,只要匹配到就直接返回退出
var regex = /ab/;
var string = "ABC ffab2 ccb wab2 ";
console.log( string.match(regex) ); 
//结果:index=6,就是第六位置匹配到了结果,并返回 input:表示输入的字符
[ 'ab', index: 6, input: 'ABC ffab2 ccb wab2 ', groups: undefined ]

g:

var regex = /ab/g;
var string = "ABC ffab2 ccb wab2 ";
console.log( string.match(regex) );  
//结果:
[ 'ab', 'ab' ]

i:

var regex = /ab/i;
var string = "ABC ffab2 ccb wab2 ";
console.log( string.match(regex) ); 
//结果:
[ 'AB', index: 0, input: 'ABC ffab2 ccb wab2 ', groups: undefined ]

m: 每一行都是一个字符串,而不是整体单独的一个字符串


var regex=/an$/;
var string="This is an\n antzone good"; 
console.log(string.match(regex));
//结果:
null

以上代码不能够匹配字符串"an",尽管"an"后面已经换行了,但是并没有采用多行匹配,所以不是字符串行的结尾。

var regex=/an$/m;
var string="This is an\n antzone good"; 
console.log(string.match(regex));
//结果:
[
  'an',
  index: 8,
  input: 'This is an\n antzone good',
  groups: undefined
]

image-20220212134125059

image-20220212134140200

s:整个文本都看成一个字符串,只有一个开头和结尾

var regex=/an$/s;
var string="This is an\n antzone good an"; 
console.log(string.match(regex));
//结果:
[
  'an',
  index: 25, //可以知道,是最后一个an匹配到了,虽然前面已经有an了,但an$是需要匹配整个字符串末尾的an的
  input: 'This is an\n antzone good an',
  groups: undefined
]

2.7 选择符和分组

2.7.1 选择符(选择分支)

具体形式如下:p1|p2,其中p1p2是子模式,之间关系是,用|(管道符)分隔,表示其中任何之一。

|:表示的是左右两个部分的,而不是单单指最近单词的或关系,比如/^I love JavaScript|Regular Expression$/,匹配字符串是"I love JavaScript"和"Regular Expression",而不是JavaScript或Regular

var regex = /good|nice/g;
var string = "good idea, nice try.";
console.log( string.match(regex) ); 
//结果:
[ 'good', 'nice' ]

注意:分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了,如下:
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) ); 
//结果 匹配到good就不在往后匹配了
["good"]

2.7.2 分组(小括号)

所有以()元字符所包含的正则表达式被分为一组,每一个分组都是一个子表达式

捕获组:(表达式)

1.在修饰匹配次数的时候,括号中的表达式可以作为整体被修饰

2.取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到

3.每一对括号会分配一个编号,根据左括号(开始自动编号。编号为0表示捕获的是由整个正则表达式模式匹配到的文本

var regex=/(ab)+d/g; //小括号可以括起来表示一个整体
var string="dab ksabd fsababd"; 
console.log(string.match(regex));
//结果:
[ 'abd', 'ababd' ]

反向引用:

1.每一对()会自动分配一个编号,可以根据编号获取()捕获的内容

2.通过反向引用,可以对分组已捕获的字符串进行引用


var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2022-02-12";
console.log( string.match(regex) ); 
//结果:可以看到,匹配到了4个结果,分别是2022-02-12,2022,02,12
[
  '2022-02-12',
  '2022',
  '02',
  '12',
  index: 0,
  input: '2022-02-12',
  groups: undefined
]

上面的结果,我们可以可以引用小括号里捕获的值,使用构造函数的全局属性$1$9来获取

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2022-02-12";
string.match(regex);
console.log(RegExp.$1); // "2022"
console.log(RegExp.$2); // "02"
console.log(RegExp.$3); // "12"

可以通过在线分析查看:

image-20220212145449737

若比价复杂的,比如

var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/;
var string1 = "2022-02-12";
var string2 = "2022/02/12";
var string3 = "2022.02.12";
var string4 = "2022-02/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // true

其中/.需要转义。虽然匹配了要求的情况,但也匹配"2022-02/12"这样的数据。若我们需要分隔符前后一致怎么办?那就要用到反向引用

var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
var string1 = "2022-02-12";
var string2 = "2022/02/12";
var string3 = "2022.02.12";
var string4 = "2022-02/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false

注意这里的\1引用的是第一个括号(-|/|.)里的值,比如匹配string1的时候,()匹配到的是-,那么\1取到的值就是-,所以d{2}后面的字符必须是-不然就匹配不上

image-20220212150252325

若是嵌套小括号的,则以左括号开始计数算起,比如/^((\d)(\d(\d)))\1\2\3\4$/

image-20220212150423293

非捕获组:(?:表达式)

一些表达式中,不得不使用(),但又不需要保存()中子表达式匹配的内容,这时候可以使用非捕获组来抵消使用()带来的副作用

var regex = /(?:ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) ); 
//结果
["abab", "ab", "ababab"]

2.8 运算符优先级

相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:

运算符 描述
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \任何元字符、任何字符 定位点和序列(即:位置和顺序)
| 替换,"或"操作 字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food"。若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood"。

2.9 回溯法原理

回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。

本质上就是深度优先搜索算法。**其中退到之前的某一步这一过程,我们称为“回溯”。**从上面的描述过程中,可以看出,路走不通时,就会发生“回溯”。即,尝试匹配失败时,接下来的一步通常就是回溯。

如果目标字符串是"abbc",中间就有回溯。

img

如上图所示,当步骤执行到第五步时,由于b{1,3},会继续去找第四个字符看看是否是b,上图中遇到了c,那么不满足,则回退到上一步,然后再执行正则表达式的下一个字符c,然后和第四个比较,正好是c,那么已经匹配到想要的字符串,就可以返回了

回溯是比较费时间的,我们可以使用具体型字符组来代替通配符,来消除回溯使用非捕获型分组,减少内存消耗提取分支公共部分减少分支的数量,缩小它们的范围等措施优化正则表达式来提高匹配效率

要认识到正则的局限,不要去研究根本无法完成的任务。同时,也不能走入另一个极端:无所不用正则。能用字符串API解决的简单问题,就不该正则出马

比如想要获取一个子串
var string = "JavaScript";
console.log( string.match(/.{4}(.+)/)[1] );
//结果:
Script

可以直接使用现成的api进行
var string = "JavaScript";
console.log( string.substring(4) );
//结果
Script

3.使用

3.1 JS版本

在 JavaScript 中,RegExp 对象是一个预定义了属性和方法的正则表达式对象。

用于正则操作的方法,共有6个,字符串实例4个,正则实例2个

String#search

String#split

String#match

String#replace

RegExp#test

RegExp#exec

//test
var regex = /ab/;
var string = "ABC ffab2 ccb wab2 ";
console.log( regex.test(string) ); //true

//exec:exec方法能接着上一次匹配后继续匹配
var regex = /ab/g;
var string = "ABC ffab2 ccb wab2 ";
console.log( regex.exec(string) );
console.log( regex.lastIndex );
console.log( regex.exec(string) );
console.log( regex.lastIndex );
[ 'ab', index: 6, input: 'ABC ffab2 ccb wab2 ', groups: undefined ]
8
[ 'ab', index: 15, input: 'ABC ffab2 ccb wab2 ', groups: undefined ]
17

//match
var regex=/(ab)+d/g;
var string="dab ksabd fsababd"; 
console.log(string.match(regex)); //[ 'abd', 'ababd' ]

3.2 JAVA版本


    @Test
    public void test1(){
        Pattern compile = Pattern.compile("([a-z]+)([0-9]+)");
        Matcher matcher = compile.matcher("aksh22**fdsk21576*23*kl");
        while (matcher.find()){
            System.out.println("matcher = " + matcher.group());
            System.out.println("matcher1 = " + matcher.group(1));
            System.out.println("matcher2 = " + matcher.group(2));
        }
        System.out.println(matcher.groupCount());
    }

image-20220212160317790

参考:

1.JS正则表达式完整教程(略长)

2.runoob

3.可能是最好的正则表达式的教程笔记了吧...