快速读懂正则表达式

一.正则表达式介绍

官方解释

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),能够添加、删除、分离、叠加、插入和修整各种类型的文本和数据。

简单理解

正则表达式就是利用规定好的语法规则,实现快速,方便地对文本进行你所需要的操作,如常见的查找,替换。

先来看看常见的游戏账号用正则表达式如何描述

​ ^[a-z0-9_-]{4,15}$

​ 一开始看可能就得非常难懂,我一开始接触正则头都大了,但其实只是不了解正则表达式罢了,本篇目标就是能学会写简单的正则表达式,享受其带来的方便和快捷。下面来解释下上面这个的含义

​ ^匹配开头,$匹配结尾,[]匹配包含括号内元素的字符 ,单个字符可以是从a到z的所有小写字母,0到9的所有数字,下划线"_"以及"-", {4,15}表示至少为四个字符,至多为15个字符


二. 正则字符简单介绍

普通字符

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

非打印字符

与常见代码中的转义字符相同,如"\n"匹配换行符

例: LOL is great

常见元字符

字符 描述 例子
^ 匹配开头 "^LOL"匹配"LOL..."
$ | 匹配结尾 | "great$"匹配"...great"
\b 只匹配一个位置,用于判断边界 "\bis\b"匹配"is"
\d 匹配数字 "\d\d\d\d"匹配四位数字
\s 匹配空白字符,如空格,换行等
\w 匹配字母,数字,下划线
. 匹配除了换行符以外的任何字符
[ ] 匹配包含括号内元素的字符 "[a-z]"匹配从a到z的所有字母
, 分割 分割条件,表示"或" "[0,2,4,6,8]"0或2或4或6或8数字

练习1(答案见最底下)

例子:"Hello,friend. My number is 007." 用正则表达式实现下列功能,括号内为练习基本要求使用的符号,实践后可自行尝试其他符号的正则书写方式

  1. 匹配"Hello" (^)
  2. 匹配"007." ($)
  3. 匹配"friend" (\b)
  4. 匹配"r is" (\s)
  5. 匹配"007" (\d)
  6. 匹配“ell” (\w或[]或.)

反义字符

符号大写,与原来意思相反

字符 描述
\B 匹配不是单词开头或结束的位置
\D 匹配任意非数字的字符
\S 匹配任意非空白字符的字符
\W 匹配任意不是字母,数字,下划线 的字符
[^ ] 匹配除了括号内元素外的字符

限定符

例子:hhhhhh,are you OK?

字符 描述 例子
* 匹配前面的子表达式0次或多次。 "h*"匹配到所有h
+ 匹配前面的子表达式一次或多次。 "h+"匹配到所有h
? 匹配前面的子表达式0次或一次。 "h?"单次匹配只匹配到一个h
n 是一个非负整数。匹配确定的 n 次。 "h{3}"单次匹配"hhh"
m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。 "h{1,6}"单次匹配可以1个h到6个h,但有多的以满足最多来获取,比如在这个例子就是匹配到"hhhhhh"
n 是一个非负整数。至少匹配n 次。 同上,只是不限制上限

这里需要引入贪婪和懒惰概念.

​ 贪婪指在要求下,若满足则尽可能多地获取,先匹配整个字符串,尝试匹配时,它会选定尽可能多的内容,如果失败则回退一个字符,直到找到匹配的内容或者没有字符可以回退

​ 懒惰指从目标的起始位置开始尝试匹配,每次检查一个字符,并寻找它要匹配的内容,如此循环直到字符结尾处

例: "lolol"

"*" 表示匹配前面的子表达式0次或多次,用

l.*l

将会匹配最长的以l开始,以l结束的字符串。如果用它来搜索上面的字符串的话,它会匹配整个字符串"lolol", 这被称为贪婪匹配。

如果在*后面加上"?",则表示懒惰匹配,将在满足条件下尽可能少的重复,如用

l.*?l

将会匹配最短的以l开头,以l结束的字符串,如果用它来搜索上面的字符串的话,它会匹配到字符串"lol"

字符 描述
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

后两个当n=0时,由于懒惰匹配,将匹配到空

练习2(答案见最底下)

用正则表达式实现下列功能,括号内为练习基本要求使用的符号,实践后可自行尝试其他符号的正则书写方式

  1. 例:"123123",匹配"123123" (.与*)
  2. 例:"123123",匹配"123" (*?与.)
  3. 例:"111111",匹配"1" (??与.)
  4. 例:"111111",匹配"11" (+?与.)
  5. 例:"111111",匹配"111" ({n,m}与.)
  6. 例:"111111",匹配"1111" ({n,m}?与.)
  7. 例:"111111",匹配全部

三.掌握正则表达式

?,*,+,\d,\w 都可以用[]{}来表示相同的作用

字符 等价于
?
*
+
\d [0-9]
\w [A-Za-z_0-9]

正则进阶-捕获分组

​ 当我们在前面进行某个正则分式匹配某个规则的字符串后,我们在后面又想匹配相同的字符串时,重新写一遍会比较麻烦与重复,于是引入了捕获分组的概念。

​ 用括号"(...)"表示一个捕获分组进行捕获,可以对捕获分组进行后向引用 (即是如果后而有相同的内容则可以直接引用前面定义的捕获分组来以简化表达式),以exp表示正则表达式

"(exp)" 匹配exp,并捕获文本到自动命名的组里

例:"123 123 234 234" 匹配到整条字符串的正则为"\b(\w+)\b\s\1\s(\w+)\s\2\b", 这里(.+)为一个捕获分组,可捕获"123",(\w+)为一个捕获分组,可捕获"234",捕获后分组自动命名从1开始," \1"捕获相同的"123"," \2"捕获相同的"234"

"(?exp)" 匹配exp,并捕获文本到名称为name的组里,后面使用时需用"\k"

既然有自动命名,当然也有手动命名

例:"123 123" 匹配到整条字符串的正则为"\b(?\w+)\b\s\k\b", 这里(?\w+)为一个捕获分组,命名为h,可捕获"123",\k调用前面捕获后一个"123"

"(?:exp)" 匹配exp,不捕获匹配的文本,也不给此分组分配组号

零宽断言:

"(?=exp)" 匹配exp前面的位置

例:"apple is my favor.",匹配到"apple"的正则:".+(?=is)"

"(?<=exp)" 匹配exp后面的位置

例:"Mike is my uncle.",匹配到"my uncle."的正则:"(?<=is).+"

"(?!exp)" 匹配后面跟的不是exp的位置

四.运用于代码中

​ 正则表达式在不同语言中核心写法都是一样的,本文主要讲解的便是核心写法,然后其写出来的正则表达式在不同语言打码中具体使用方式不同,在代码中一般为了使程序知道"\"需要用两次\\

​ 首先是我用起来最方便(因为只需要写正则表达式,它能一键生成选择的语言的代码以供使用),现在也已经免费的正则表达式工具 Match Tracer:http://www.regexlab.com/zh/mtracer/

​ 你也可以使用很多在线的正则表达式生成网站(可自行搜索,一般都免费)

​ 如: https://c.runoob.com/front-end/854 还有常用的正则表达式

C++中的正则

最简单的匹配与否用法

头文件中的regex_matchregex_search均可以进行匹配,返回一个布尔类型,匹配成功为true,匹配失败为false。

不同点:前者要求完全匹配,后者要求子串匹配即可;

string str = "hhh233";
regex r("[a-z0-9]+");

//直接写好一个匹配规则判断字符串匹配与否
// 用法一
bool test1 = regex_match(str, r);
// 用法二
bool test2 = regex_match(str, regex("hhh\\d+"));

if(test1)
	cout << "test1 is yes\n";
if (test2)
	cout << "test2 is yes\n";
//Output:
//test1 is yes
//test2 is yes

Java

public static void main(String[] args) 
{
	String str[] = {"521000","021-1234567","19987654321"};
	//我的邮编是:521000电话号码是:021-1234567,手机号码:19987654321";
	String pattern = "\\d{6}";		//邮编
	String pattern1="\\d{3,4}\\-\\d{7,8}";	//区号+座机号
	String pattern2="(13|14|15|17|18|19)[0-9]{9}";
	
	for(int i=0;i<3;i++)
	{
		if(str[i].matches(pattern))
		{
			System.out.println(str[i]+"是邮编");
		}
		else if(str[i].matches(pattern1))
		{
			System.out.println(str[i]+"是电话号码");
		}
		else if(str[i].matches(pattern2))
		{
			System.out.println(str[i]+"是手机号");
		}
	}

}
//Output:
//521000是邮编
//021-1234567是电话号码
//19987654321是手机号

最后你也可以看看下面两篇文章,讲述了我平时所用的C#和C++如何使用正则表达式

C++如何使用:https://blog.csdn.net/bgzclxqq/article/details/90262904

C#如何使用:https://blog.csdn.net/qq_35970739/article/details/82740272

练习答案

练习1

  1. ^Hello
  2. 007.$
  3. \bfriend\b
  4. r\sis
  5. \d\d\d
  6. \wll [e]ll e.l

练习2

  1. 1.*?3

  2. 1.?23

  3. 1.??

  4. 1.+?

  5. .

  6. .{4,6}?

  7. .+ 或 1{1,6} 或 。。。

以上练习答案不唯一,你可以使用该网站进行测试你的答案是否正确:https://tool.oschina.net/regex/

只需要将练习题复制在test string处,然后在regular expression处输入你的答案看是否匹配成功即可

posted @ 2020-04-17 22:39  AMzz  阅读(548)  评论(0编辑  收藏  举报
//字体