看完这一篇,学会写正则表达式

308 阅读4分钟

什么是正则表达式:

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

用途:

正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本

如果觉得前面的这些基础知识看着比较枯燥可以直接翻到最后看我精心准备的两个示例,看示例过程中有迷惑的地方再回头看这些基础知识

元字符:

字符说明
.匹配除换行符(\n、\r)之外的任何单个字符
\d匹配一个数字字符
\D匹配一个非数字字符
\n匹配一个换行符
\r匹配一个回车符
\s匹配任何空白字符,包括空格、制表符、换页符等等
\w匹配字母、数字、下划线
\W匹配非字母、数字、下划线
匹配输入字符串的开始位置
$匹配输入字符串的结束位置

量词:

量词说明示例
*0次或多次,可以没有ab* 可以匹配 a或abbb
+一次或多次,至少一次ab+ 可以匹配ab或abbbb 但不能匹配 ab
0次到1次,至多一次colou?r 可以匹配 color 和 colour
{n}出现n次
{n,}至少出现n次
{n,m}出现n到m次 包含 n和m

范围:

范围说明示例
[...]多选一,匹配括号中的任意元素[ab] 代表 当前位置可以是 a 或 b
[a-z]匹配a-z之间的任意元素 包含 a-z
[^...]取反,不能是括号中的任意元素

分组:

括号在正则中可以用于分组,被括号括起来的部分“子表达式”会被保存成一个子组。

编号:按照顺序从 1开始为分组编号

不保存子组:只用于分组,后续不在使用这个子组的内容,可以使用 (?:正则)

分组嵌套:按照左括号出现的顺序为分组编号 命名分组:对分组进行命名,可以通过名称引用分组中的内容,相对于数字编号更容易识别和使用。 格式:(?P<分组名称>正则)

使用Java代码通过命名组提取数据代码示例如下:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
    public static void main(String[] args) {
        final String regex = "(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2}) (?<hour>\\d{2}):(?<minute>\\d{2}):(?<second>\\d{2})";
        final String string = "2020-10-16 12:15:30\\n";

        final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
        final Matcher matcher = pattern.matcher(string);
        System.out.println("年:"+matcher.group("year"));
    }
}

使用Java代码通过分组替换数据示例代码:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
    public static void main(String[] args) {
        final String regex = "(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})";
        final String string = "2020-10-16 12:15:30\\n";
        final String subst = "$1年$2月$3日 $4时$5分$6秒";

        final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
        final Matcher matcher = pattern.matcher(string);
        
        final String result = matcher.replaceAll(subst);
        System.out.println("Substitution result: " + result);
    }
}

匹配模式

使用模式修饰符 (?模式修饰符)正则

不区分大小写模式(Case-Insensitive)

(?i)正则: 忽略大小写

点号通配模式(Dot All)

(?s)正则: 英文. 可以匹配任意字符

多行匹配模式(Multiline)

(?m)正则: 使^可以匹配一行文本开头 $可以匹配到一行文本的结尾

注释模式

(?#comment):向正则中增加注释,方便阅读。

断言

断言是指对匹配到的文本位置有要求。

单词边界(Word Boundary)

\\b:匹配单词边界 ,例如 er\b 可以匹配到 never中的 er ,但不能匹配到 very中的er。

行的开始或结束

^:行开头

$:行结尾

换行符在不同平台中的表示:

环视(Look Around)

环视就是要求匹配部分的前面或后面要满足(或不满足)某种规则,有些地方也称环视为零宽断言。

例如我们需要在一个短信中匹配出一个 4-6位的验证码

很容易写出这个正则 \d{4,6}

但是如果是一个物流公司给你发来了物流发出提醒:

您好,您的包裹已通过XX快递寄出,运单号:1223445567676059

这样一个短信也会被匹配为一个验证码短信。

正则是通过环视来解决这个问题

语法:

正则名称含义
(?<=Y)肯定逆序环视(postive-lookbehind)左边是Y
(?<!Y)否定逆序环视(negative-lookbehind)左边不是Y
(?=Y)肯定顺序环视(postive-lookahead)左边是Y
(?!Y)否定顺序环视(negative-lookahead)左边不是Y

上述的正则就可以修改为(?<!\d)\d{4,6}(?!\d)。 这个正则匹配 4到6 个数字并且这 4到6 个数字左边不能是数字右边也不能是数字

转义

正则中的常见转义字符及其含义:

元字符转义

查找星号 *、加号 + 、问号 ? 在上述字符前面增加反斜杠\

括号转义

查找方括号 [ ] 和花括号 { } 时 需要对开括号 [ { 进行转义,圆括号 ( ) 两个都需要转义。

字符组中的转义

  • 脱字符 ^ 在括号中,且在第一个位置需要转义
  • 中划线 - 在括号中,且不在首位需要转义
  • 右方括号 ] 在括号中,且不在首位需要转义

工具

学习网站: regex101

image.png

示例一:截取验证码短信中的信息

短信验证码常用于身份验证,例如登陆,找回密码,注册等场景。

下面是两条验证码短信

【掘金】验证码1234,用于手机验证码登录,5分钟内有效。验证码提供给他人可能导致帐号被盗,请勿泄露,谨防被骗。

【阿里云盘】验证码:123456。此验证码只用于短信登录,15分钟内有效

假设我们要编写一个正则来获取短信验证码来源 ,验证码,有效期等数据。

  1. 匹配来源:通常来说如果是云平台的短信服务,那么来源的数据就一定包含在 一对中文的方括号中,名称不能为空。使用【.+】 就可以很容易匹配到来源
  2. 匹配验证码:验证码通常来说是 4-6 位的数字,可以用 \\d{4,6},考虑到准确性我们增加一些断言信息,修改后使用 (?<!\\d)\\d{4,6}(?!\\d)
  3. 匹配有效期:有效期通常是XX分钟或小时内有效 ,可以使用 \\d{1,2}(分钟|小时)内有效

能够识别出来后,我们还要将这些数据提取出来,这里就用到了分组,我们再需要的数据上加上括号和名称之后:

  • 来源:【(?<source>.+)】
  • 验证码:(?<!\\d)(?<code>\\d{4,6})(?!\\d)
  • 有效期:(?<expire>\\d{1,2}(分钟|小时))内有效

最后在不确定的位置增加上 .+ 占位 0个或多个字符,并且按照顺序拼接起来:

【(?<source>.+)】.+(?<!\\d)(?<code>\\d{4,6})(?!\\d).+(?<expire>\\d{1,2}(分钟|小时))内有效

示例二:截取一个语音下单指令中的信息

一瓶雪碧

二瓶可乐

十五瓶芒果汁

三十五袋薯片

206瓶西瓜汁

十五瓶雪碧

我们可以观察这个格式 可以得出 这些文本的格式都是 数量+两次+商品名称

我们假设我们可以识别的商品名称仅有上面指令中的这些即雪碧,可乐,芒果汁,西瓜汁,薯片

且我们只考虑有限的量词即 ,, 于是 我们就可以很容易的写出一个正则来解析上面的文本

(?<num>[一二三四五六七八九十百千万两仨贰\d]+?)(?:瓶||袋)(?<goodsName>雪碧|可乐|芒果汁|薯片|西瓜汁)

这样我们就可以很容易的通过 numgoodsName 来获取想要购买的商品数量和名称。

以上就是本篇文章的全部内容,有错误的地方欢迎批评指正。 看完之后如果依旧有遇到不会写的正则,欢迎在评论区留言。