正则表达式(regular expression)

一、文章概览-正则表达式(regular expression)

(一)什么是正则表达式

        略

(二)正则表达式的匹配语法规则的三大模块

1、匹配字符(1个字符/字符串)

        普通字符、特殊字符、标准字符集合、自定义字符集合

2、匹配数量(多个字符/字符串)

        量词表达式

3、匹配相对位置

        零宽表达式

(三)练习

(四)开发环境使用正则表达式

二、普通字符和特殊字符本身的匹配

普通字符本身的匹配

        普通字符,用于匹配字符、匹配数量。

        字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是普通字符。表达式中的普通字符,在匹配字符串的时候,匹配字符本身,匹配字符数量为一个

(二)特殊字符本身的匹配

        简单的转义字符组合:转义字符+特殊字符,用于匹配字符、匹配数量。

\n

匹配一个换行符

\t

匹配一个制表符

\\

匹配一个\字符

\^,\$, \. , \(, \),\{,\},\?,\+,\*,\|,\ [,\ ]

匹配转义字符\之后的那个特殊字符本身,匹配一个。

        转义字符+特殊字符组合:匹配\之后的特殊字符本身,匹配字符数量为一个。    

1、什么是特殊字符

正则表达式中的特殊字符区别于普通字符,在正则表达式中有特殊功能不匹配字符本身在此场景下,若要匹配特殊字符本身,需要在其前加转义字符“\形成简单的转义字符组合再进行匹配

eg: 字符 - ,放在中括号内使用时表示连接其前后的两个的字符范围的并集,若在中括号内要表示 - 字符本身,需要加上转义字符“\”,反之在中括号外为普通字符、要表示 - 本身,则无需加转义字符。

2、\n \r \t 的区别

php正则表达式中\r,\n,\r\n的区别 - php91 - 博客园

\n是换行,英文是New line,表示使光标到行首

\r是回车,英文是Carriage return,表示使光标下移一格

\r\n表示回车换行我们在平时使用电脑时,已经习惯了回车和换行一次搞定,敲一个回车键,即回车,又换行。

(1)\n 软回车:

在Windows 中表示换行且回到下一行的最开始位置。相当于Mac OS 里的 \r 的效果。

在Linux、unix 中只表示换行,但不会回到下一行的开始位置。

(2)\r 软空格:

在Mac OS 中表示换行且返回到下一行的最开始位置,相当于Windows 里的 \n 的效果。

在Linux、unix 中表示返回到当行的最开始位置。

(3)\t 跳格(移至下一列)。

它们在双引号或定界符表示的字符串中有效,在单引号表示的字符串中无效。

\r\n 一般一起用,用来表示键盘上的回车键,也可只用 \n。

\t表示键盘上的“TAB”键。

就像你使用 enter和shift+enter的区别

(4)文件中的换行符号:

linux,unix: \r\n

windows : \n

Mac OS : \r

三、标准字符集合

        标准字符集合,用于匹配字符、匹配数量。

        四种(七个)标准字符集合,大写、小写匹配的集合为两互斥的集合。

        注意:一个标准字符集合是多个匹配条件的并集,匹配满足条件集的字符,匹配字符数量为一个。

(一)\d与\D

        \d,匹配0-9中的任意一个数字。

        \D,匹配与\d互斥的字符集合,匹配字符数量为一个

(二)\w与\W

        \w,匹配任意一个字母、数字、下划线,即匹配A-Z a-z 0-9 _ 组成的字符并集内的任意一个字符。

        \W,匹配与\w互斥的字符集合,匹配字符数量为一个

(三)\s与\S

        \s,匹配空白字符集合中的任意一个空白字符,即匹配空格、制表符、换行符并集内的任意一个字符。

        \S,匹配与\s互斥的字符集合,匹配字符数量为一个

(四) (英文句点)

        匹配除了换行符(\n)之外的任意一个字符。

        注意:一般用[\s\S]匹配包含换行符在内的所有字符并集内的任意一个字符。

四、自定义字符集合

        自定义字符集合,用于匹配字符、匹配数量。

        自定义字符集合的关键表示符号为中括号:[ ]

(一)什么是自定义字符集合

        区别于标准字符集合,用户自己指定的匹配条件的并集就是自定义字符集合,自定义字符集合具体条件表达式(匹配条件)用中括号[ ]括起。

(二)匹配逻辑

        匹配字符时,对中括号[ ]内的字符取并集,即“”的运算逻辑。

(三)匹配表达式注意事项

1、普通字符与特殊字符           

        字符 ^ 表示中括号整体条件并集合的互斥集合,在中括号内要表示 ^ 本身,则需要加转义字符,即 \^

        除此之外,在本文:二(二)中列出的特殊字符放在中括号[ ]内时成为普通字符。

        在中括号内[ ],当 - 前后连接的两个字符同时为字母或同时为数字时,表示连接其前后的两个字符范围的并集,否则在中括号内[ ], - 表示其符号本身。

2、标准字符集合

        . (英文句点)在中括号[ ]内成为普通字符,在中括号[ ]外表示一个标准字符集。匹配字符时,除了 . (英文句点)剩下6个标准字符集合如果被包含于中括号[ ],自定义字符集合将包含该标准字符集合。

        eg:[\d.\-+]将匹配数字、 . (英文句点)、+、- 中的任意一个字符。

(四)匹配数量

        匹配数量时,匹配满足中括号[ ]内给定条件的字符并集中的任意一个字符。

五、量词表达式

        量词表达式,用于匹配数量。

        量词表达式的关键表示符号为花括号:{ }

(一)什么是量词表达式

        量词表达式,是修饰字符匹配次数的特殊字符。当进行字符匹配时,用于进一步指定对应字符连续出现的次数。具体数量表达式用花括号{ }括起。

(二)量词表达式规则

        量词可以修饰单个字符、多个字符、单个字符集、多个字符集。多个字符或多个字符集用小括号括起再紧跟量词表达式,则多个字符或者多个字符集同时被一个量词表达式修饰。

{n}

达式连续出现n

{mn}

表达式最少连续出现m次,最多连续出现n次

{m,}

表达式至少连续出现m次

?

表达式连续出现0次或者1次,相当于{0,1}

+

表达式至少连续出现1次,相当于{1,}

*

表达式连续出现任意次,相当于{0,}

         + 在中括号内表示普通字符,在中括号外为量词表达式。

        {N}限定固定的数量,匹配时,数量不可浮动。

        {m,n}限定浮动数量,匹配时,数量在指定范围内浮动。其中n可以缺省,m不可缺省逗号不能省略,限定最少次数、不限定最多次数最少出现次数值不可缺省,最多出现次数值可以缺省。

(三)量词匹配的两种模式

1、贪婪模式

匹配字符越多越好,优先匹配量词表达式的最大值,默认的匹配模式。

2、勉强模式

匹配字符越少越好,优先匹配量词表达式的最小值,在修饰匹配次数的特殊符号后再加上一个 ? 号。

3、量词匹配模式例子

(1)\d{6}

        表示匹配6个连续数字的字符串

(2)\d\d{6}

        表示匹配7个连续数字的字符串(1+6=7)

(3)(\d\d){6}

        表示匹配12个连续数字的字符串(2X6=12)

(4)\d{3,6}

        表示匹配3到6个(即:3个或4个或5个或6个)连续数字的字符串,优先匹配连续出现次数较的字符串。默认情况下就是“贪婪模式”。

(5)\d{3,6}? 

        表示匹配3到6个(即:3个或4个或5个或6个)连续数字的字符串,优先匹配连续出现次数较的字符串。在量词表达式之后加 ? 号,即所谓的“非贪婪模式”或叫做“勉强模式”。

六、零宽表达式

        零宽表达式,用于匹配相对位置。

        零宽表达式:可用于匹配相对位置或者匹配内容不计入最终结果的正则表达式。

(一)什么是零宽表达式

        正则表达式匹配过程中,如果子表达匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么认为这个子表达式是占有字符的;如果子表达式匹配的仅仅是相对位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的,该表达式称为零宽表达式一个正则表达式是占有字符还是零宽度,是针对匹配的内容是否为相对位置、或者是否保存到终的匹配结果中而言的。

1、匹配的内容不保存

        只进行子表达式的匹配,匹配内容只作为中转引用、不计入最终的匹配结果,是零宽度。

2、匹配结果是相对位置而非占位符

        是相对位置的匹配,这个位置应该符合某个条件:当前被匹配字符位置的前、后第一个字符,应该符合零宽表达式指定的条件,但最终结果不匹配其前、后的字符。

(二)三种边界字符

^

与字符串开始的位置匹配

$

与字符串结束的位置匹配

\b

所匹配字符串需满足的条件:边界上的第一个字符不是\w(字母、数字、下划线)

1、什么是零宽

        边界字符匹配的结果不是字符或字符串,而是一个满足条件的相对位置。因此,匹配结果是非占位符,零宽度的,仅标识匹配字符开始位置或结束位置。

2、作用场景

        边界字符需放在中括号[ ]外面才能起匹配相对位置的作用,例如:^ 如果放在中括号[ ]里面则表示取反/互斥的功能,则无法起到匹配相对位置的作用。

3、用法举例

(1)\blove\b

        表示匹配一个字符串love且该单词的开头和结尾的第一个字符都不是\w(字母、数字、下划线)的字符串love。

(三)预搜索(零宽断言/环视)

(?=expression)

断言自身出现的位置的后面能匹配表达式expression

(?<=expression)

断言自身出现的位置的前面能匹配表达式expression

(?!expression)

断言自身出现的位置的后面不能匹配表达式expression

(?

断言自身出现的位置的前面不能匹配表达式expression

1、[a-z]+(?=ing)

        表示匹配包含至少一个小写字母且以ing结尾的字符串(匹配内容要剔除掉结尾的ing,再作为最终结果)。

2、[a-z]+(?!ing)

        表示匹配包含至少一个小写字母且不以ing结尾的字符串。

(四)匹配模式

1、IGNORECASE

        匹配时忽略大小写,case insensitive。默认情况下,正则表达式是要区分大小写的。

2、SINGLELINE 单行模式

        整个文本看作一个字符串进行匹配,只有一个开头和一个结尾。此时符号 .(英文句点)可以匹配包含换行符(\n)在内的任意一个字符。

3、MULTILINE多行模式

        每行都是一个字符串,都有开头和结尾,遇到换行符即表示另外一行字符串,此时符号 .(英文句点)无法匹配换行符(\n)。

        在多行模式下 \AXXX 表示在整个文本的开头行(第一行)匹配XXX字符串,等价于单行模式下的 ^XXX

        在多行模式下 XXX\Z 表示在整个文本的结尾行(最后一行)匹配XXX字符串,等价于单行模式下的 XXX$

        ^ $ 用于单行模式,\A \Z 用于多行模式。

(五)分组捕获与反向引用

1、分组捕获

表达式

作用

|

分支结构

左右两边表达式之间“或”关系,匹配左边或者右边。

()

捕获组

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

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

3、每一对括号会分配一个编号,使用小括号( )的捕获根据左括号的顺序从1开始自动编号。捕获元素编号为0的第一个捕获是由整个正则表达式模式匹配的文本。

?:Expression

非捕获组

一些表达式中,不得不使用小括号(),但又不需要保存小括号()中子表达式匹配的内容(保存是为了用于反向引用),这时可以用非捕获组来抵消使用()带来的副作用(小括号()内的正则表达式的匹配内容会保存至内存)。

2、反向引用

        反向引用表达式:((expression1)(expression2))\n\i\j,其中n,i,j为数字,表示引用的内容出现的次数。此表达式中,编号为0和1的分组所捕获的内容一样为整个表达式所捕获匹配的最终内容、expression1expression2整体出现n次,编号为2的分组捕获的内容为连续出现 次的expression1,编号为3的分组捕获的内容为:连续出现 次的expression2。

        每一对小括号()会分配一个编号,使用小括号()的捕获根据小括号的顺序从1开始自动编号。

        通过反向引用可以对分组已捕获的字符串进行引用再匹配。

3、用法举例

(1)([a-z]{2})\3

        表示匹配2个a~z字符的字符串,并且该字符串连续出现3。详细分析:即小括号(Expression)匹配的结果作为一个整体先存到内存中,后被“\3”进行引用修饰再进行二次匹配最终得到想要的结果。如:go,其中的go两个字符为一个整体被“\3”引用修饰再匹配,结果匹配字符串为gogogo。

(2)(?:[a-z]{2})\3

        无法匹配任何结果,因为小括号内加了?:抵消了分组捕获得到的结果的保存,即没有对分组得到的结果进行保存,所以“\3”没有修饰引用的对象,所以无法匹配到任何结果。符号“?:”不对(expression)匹配结果进行产生影响,但是会让(expression)匹配的结果不保存到内存中,无法再次被引用匹配。

(3)((Expression1)(Expression2))(Expression3)\i\j\k\m

        使用小括号()分组捕获然后进行反向引用,根据左括号的顺序从1开始自动编号,对已分组捕获的字符串进行引用。

A、\i修饰的对象是Expression1和Expression2匹配的结果的整体重复出现i次

B、\j修饰的对象是Expression1匹配的结果的整体重复出现j次

C、\k修饰的对象是Expression2匹配的结果的整体重复出现k次

D、\m修饰的对象是Expression3匹配的结果的整体重复出现m次

七、练习

(一)电话号码验证

1、思路分析

(1)电话号码由数字和符号 - 构成

(2)电话号码为7到8位

(3)如果电话号码中包含有区号,那么区号为三位或四位,首位是0

(4)区号用符号 - 和其他部分隔开

(5)移动电话号码为11位

(6)11位移动电话号码的第一位和第二位为"13","15","17","18","19"

2、最终表达式

(1)固话号码:0\d{2,3}-\d{7,9}

        区号以0开头,区号与号码用 - 连接,号码为7~9位

(2)移动电话号码:1[35789]\d{9}

        以1开头,第二位为35789中的任意一个数字,总共有11位数字。

(3)电话号码验证最终正则表达式:(0\d{2,3}-\d{7,9})|(1[35789]\d{9}) 

        综合匹配固话或者移动电话,逻辑或运算。

(二)电子邮箱验证

1、思路分析

(1)用户名∶字母、数字、中划线、下划线组成。

(2)@

(3)网址∶字母、数字组成。

(4)小数点∶.

(5)组织域名∶2-4位字母组成。

(6)不区分大小写

2、步骤结果

(1)用户名,字母、数字、下划线、中划线,长度不定,至少一个字符,字母不区分大小写:[\w\-]+

(2)网址,字母、数字,长度不定,至少一个字符,字母不区分大小写:[a-zA-Z0-9]+

(3)组织域名,由2~4位字母组成,不区分大小写:\.[a-zA-Z]{2,4}

3、最终结果

        电子邮箱验证最终正则表达式:[\w\-]+@[a-zA-Z0-9]+(\.[a-zA-Z]{2,4}){1,2}

        eg:[email protected] [email protected]

(三)常用正则表达式列表

匹配中文字符

[\u4e00-\u9fa5]

匹配空白行

\n\s*\r

匹配HTML标记

<(\S*?)[^>]*>.*?</\1>|<.*? />

匹配首尾空白字符

^\s*|\s*$

匹配Email地址

\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

匹配网址URL

[a-zA-z]+://[^\s]*

匹配国内电话号码

\d{3}-\d{8}|\d{4}-\d{7}

匹配腾讯QQ

[1-9][0-9]{4,}

匹配中国邮政编码

[1-9]\d{5}(?!\d)

匹配身份证

\d(15)|\d(18)

匹配ip地址

\d+\.\d+\.\d+\.\d+

        注意:

        匹配HTML标记的正则表达式中,Expression2 表示搜索非自闭标签,Expression1 即指定要搜索的html标签,Expression2 即指定要搜索的标签内容。

        匹配中国邮政编码正则表达式中,(?!\d) 即零宽断言,表示匹配的结果字符串后面紧跟着的字符为非数字。

        总结:

        再复杂的正则表达式均由~所列出的正则语法规则组成,所以重点是掌握上述所列的基本语法,基础要牢。

八、开发环境中使用正则表达式

(一)在文本编辑器或者开发工具软件中使用正则表达式

        eclipse、Notepad++、Editplus、UltraEdit

(二)数据库中也可以使用正则

        Mysql5.5以上、Oracle10g以上

        例如∶

SELECT prod_name

FROM products

WHERE prod_name REGEXP '.000'

(三)在java程序代码中使用正则表达式

        相关类位于:java.util.regex包下,jdk自带jar包。

        Pattern类:正则表达式的编译表示形式。

// 建立正则表达式,并启用相应模式
Pattern p = Pattern.compile("expression");

Matcher类:执行匹配操作的引擎。

        通过解释 Pattern 对 character sequence 执行匹配操作的引擎

// 建立正则表达式,并启用相应模式
Pattern p = Pattern.compile("expression"); 

//匹配string字符串
Matcher matcher = p.matcher("String"); 

boolean flag=matcher.matches();

1、字符串操作

(1)测试正则表达式对象的基本用法及其三个常用方法测试

 package com.study.regex.test;
 ​
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: 测试正则表达式对象的基本用法及其三个常用方法测试:
  * matches();--判断给定字符串整体是否与该给定的正则表达式匹配
  *      返回布尔值
  * find();--判断给定字符串内是否包含与该给定的正则表达式匹配的下一个子序列
  *      返回布尔值
  * group();--分组捕获功能,查找出给定字符串内所有与给定正则表达式匹配的子字符串。
  *      返回匹配的子字符串
  * @author: LinZiyi
  * @date: 2022/03/01 001311:18
  */
 public class Demo01_Search {
     public static void main(String[] args) {
 //        创建pattern对象,并给定匹配模式 \w 字母、数字、下划线
         Pattern p=Pattern.compile("\\w+");
 //        创建matcher对象,并给定要匹配的字符串
         String string="aslfdkja18483017";
         Matcher matcher=p.matcher(string);
 //        mathces();--将整个字符串序列看成一个整体与该给定的正则表达式匹配
         boolean flag=matcher.matches();
         System.out.println(flag);
         String string2="aslfdkja&&**18483017";
         matcher=p.matcher(string2);
         boolean flag2=matcher.matches();
         System.out.println(flag2);
         System.out.println("==============");
 //        find();--将整个字符串拆分成多个子序列,
 //        查找与该给定的正则表达式匹配的下一个子序列
         String string3="aslfdkja&&**18483017";
         matcher=p.matcher(string3);
 /*      boolean flag_find=matcher.find();
         boolean flag_find1=matcher.find();
         boolean flag_find2=matcher.find();
         System.out.println(flag_find);//true
         System.out.println(flag_find1);//true
         System.out.println(flag_find2);//false
 */
         boolean flag_find;
         while (flag_find=matcher.find()){
             System.out.println(flag_find);
 //        group();--分组捕获功能,匹配整个表达式的子字符串。
             System.out.println(matcher.group());
         }
         System.out.println(flag_find);
     }
 }

(2)正则表达式中分组的处理

 package com.study.regex.test;
 ​
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: 测试正则表达式中分组的处理
  *      可用于从邮箱地址中截取邮箱账号等场景。
  * @author: LinZiyi
  * @date:2022/03/01 001312:19
  */
 public class Demo02_Group {
     public static void main(String[] args) {
 //        创建pattern对象,并给定匹配模式
 //        ([a-z]+)([0-9]+) 分组1:[a-z]+  分组2:[0-9]+
         Pattern p=Pattern.compile("([a-z]+)([0-9]+)");
 //        创建matcher对象,并给定要匹配的字符串
         String string="aslfdkja&&18483017**fkj2382fdsg";
         Matcher matcher=p.matcher(string);
         while (matcher.find()){
             System.out.println(matcher.group());//fkj2382
             System.out.println(matcher.group(1));//fkj
             System.out.println(matcher.group(2));//2382
         }
         System.out.println("=============");
         String string2="adfd324**asdf8592&&aklfjda89734";
         matcher=p.matcher(string2);
         while (matcher.find()){
             System.out.println(matcher.group());//([a-z]+)([0-9]+)
             System.out.println(matcher.group(1));//([a-z]+)
             System.out.println(matcher.group(2));//([0-9]+)
             /*
             第一次循环:
                 adfd324--group();
                 adfd   --group(1);
                 324    --group(2);
             第二次循环:
                 asdf8592
                 asdf
                 8592
             第三次循环:
                 aklfjda89734
                 aklfjda
                 89734
             */
         }
 ​
     }
 }

(3)使用正则表达式实现字符串的替换功能

 package com.study.regex.test;
 ​
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: 使用正则表达式实现字符串的替换功能
  *      replaceAll();
  * @author: LinZiyi
  * @date: 2022/03/01 001312:21
  */
 public class Demo03_Replace {
     public static void main(String[] args) {
         String string="adfd324**asdf8592&&aklfjda89734";
         Pattern pattern=Pattern.compile("[0-9]+");
         Matcher matcher=pattern.matcher(string);
         String new_string = matcher.replaceAll("%Replace%");
         System.out.println(string);
         System.out.println(new_string);
     }
 }

(4)使用正则表达式实现字符串切割功能

 package com.study.regex.test;
 ​
 import java.util.Arrays;
 ​
 /**
  * @description: 使用正则表达式实现字符串切割功能
  *      string.split("regular expression");
  * @author: LinZiyi
  * @date: 2022/03/01 001313:19
  */
 public class Demo04_Split {
     public static void main(String[] args) {
         String string="adfd324**asdf8592&&aklfjda89734";
         String[] split = string.split("[^a-z]+");
         System.out.println(string);
         System.out.println(split.length);
         System.out.println(Arrays.toString(split));
         String[] split1 = string.split("[^a-z]");
         System.out.println(split1.length);
         System.out.println(Arrays.toString(split1));
         System.out.println("============");
     }
 }

2、网络爬虫

(1)获取指定网页内所有链接网址

 package com.study.regex.test;
 ​
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 ​
 /**
  * @description: No more nonsense,show me the code!
  * @author: LinZiyi
  * @date: 2022/03/01 001314:49
  */
 public class WebSpider {
     public static void main(String[] args) {
         String link="https://www.jd.com/";
         String urlString = getUrlString(link, "utf-8");
 //        String regex="href=\"(http[\\w\\s./:]+?|//www[\\w\\s./:]+?)\"";
         String regex="href=\"([\\w\\s./:]+?)\"";
         List link_list = getMatcherLink(urlString,regex);
         System.out.println(link_list.size());
         for (String link_string : link_list) {
             System.out.println(link_string);
         }
     }
     public static String getUrlString(String urlstring,String charset){
         String string=null;
         try {
             URL url=new URL(urlstring);
             InputStream inputStream = url.openStream();
             BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream, Charset.forName(charset)));
             StringBuffer stringBuffer=new StringBuffer();
             String read;
             while ((read=bufferedReader.readLine())!=null){
                 stringBuffer.append(read);
                 stringBuffer.append("\r\n");
             }
             bufferedReader.close();
             string = stringBuffer.toString();
         } catch (MalformedURLException e) {
             e.printStackTrace();
         } catch (IOException e) {
             e.printStackTrace();
         }
         return string;
     }
     public static List getMatcherLink(String urlString,String regex){
         //        获取整个超链接的内容
         //  Pattern pattern=Pattern.compile("[\\s\\S]+?");
         //  获取超链接的href属性及其属性值
         Pattern pattern=Pattern.compile(regex);
         Matcher matcher=pattern.matcher(urlString);
         List link_list=new ArrayList<>();
         while (matcher.find()){
             link_list.add(matcher.group(1));
         }
         return link_list;
     }
 }

你可能感兴趣的:(正则表达式)