正则校验-我需要的正则表达式知识

正则校验:我需要的正则表达式知识

正则表达式由正则表达式引擎提供支持,不同编程环境有不同的正则表达式引擎,在实际使用正则表达式的过程中会有一些差别;


什么是正则表达式

正则表达式是用于描述匹配复杂字符串规则的工具,一个正则表达式对应着一个文本规则;


EP1:查找一段文本中的字符串hq,这是一个姓名的缩写,需要是单独的一个单词

  • 精确匹配字符串hq,处理正则时一般支持忽略大小写的选项,如果选中,那么就可以匹配到hq,HQ,Hq,hQ四种;
  • 如果直接查找字符串HQ,那么可能匹配到其他包含该字符串的单词,而我们要的是精确匹配;
//精确匹配单词hq
"\bhq\b"

\b 是正则支持的特殊代码,也叫元字符(metacharacter),表示单词的分界处,即单词的开头和结尾位置;


EP1-1:hq后面不远处跟着一个dm,应该使用如下正则表达式

//hq 任意个任意字符(但不能有换行) 最后还是dm这个单词
"\bhq\b.*\bdm\b"

. 也是一个元字符,可以匹配除了换行符以外的任意字符;

* 同样是元字符,它不代表字符或位置,而是代表数量,表示*前面的内容可以连续重复使用任意次(可能0次);


EP2:以0开头 然后是两个数字 然后是一个连字符“-”,最后是8个数字

"0\d\d-\d\d\d\d\d\d\d\d"

\d 元字符,匹配一位数字;

-不是元字符 只匹配自己;


EP2-1:为了避免重复,可以修改正则如下

"0\d{2}-\d{8}"

这里{n} 表示前面的\d必须连续重复匹配2次/8次;


元字符

现在我们已经知道了\b.*\d这几个元字符,还有更多,我们结合例子一起看;

\s 元字符,匹配任意的空白符,包括 空格、制表符(Tab)、换行符、中文全角空格等;

\w 元字符,匹配字母或数字或下划线或汉字等;


EP3:匹配以字母a开头的单词

"\ba\w*\b"

EP3-1:匹配刚好6个字符的单词

"\b\w{6}\b"

EP4:匹配一个或多个连续的数字

"\d+"

+ 元字符,和*类似,表示匹配重复1次或更多次;


EP5:填写的QQ号必须为5到12位的数字

"^\d{5,12}$"

^ 元字符,匹配字符串的开始位置;

$ 元字符,匹配字符串的结束位置;

这里使用了^$,则表示校验的整个字符串从开始到结束,都要用\d{5,12}进行匹配;如果不使用,只能保证字符串中包含5到12位的连续数字;

这里的{5,12}表示重复次数不少于5,不大于12;

处理正则时一般支持处理多行的选项,如果选中,^$就表示匹配行的开始和结束位置;【?


JavaScript中的正则表达式

参考JavaScript RegExp 对象;


字符转义

如果要查找元字符本身的话,需要使用\来取消这些符号的特殊意义,如\.\*\\

例如:匹配C:\Windows 使用 C:\\Windows;匹配deerflower.cn使用deerflower\.cn


重复

代码/语法说明
*重复0次或多次
+重复1次或多次
?重复0次或1次
{n}重复n次
{n,}重复n次或多次
{n,m}重复n次到m次

例如:匹配一行/整段字符串的第一个单词 使用^\w+


字符类

对于数字 字母 空白符等已经有了对应这些字符集合的元字符,但如果想匹配自定义的字符集合,可以使用方括号;


EP6:匹配任何一个英文元音字母

"[aeiou]"

EP6-1:匹配三个标点符号中的一个

"[.?!]"

EP7:匹配字符范围 0-9 中的一个

"[0-9]"//与\d完全一致

EP7-1:匹配字符范围 0-9大小写字母 中的一个

"[a-z0-9A-Z]"//只考虑英文的话,与\w完全一致

请尝试分析下这个正则:\(?0\d{2}[) -]?\d{8}

  • () 都是元字符,所以这里用了转义;
  • 首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)-空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})

上边这个正则会匹配到010)12345678(022-87654321 这样的格式(我们所希望的是匹配(010)88886666,或022-22334455,或029 12345678这样的格式),解决这个问题需要用到分支条件;


分支条件

分支条件指有几种规则,满足其中任意一个就算匹配成功,具体使用|把不同的规则分隔开;


分析正则 0\d{2}-\d{8}|0\d{2}-\d{7}

  • 可以匹配到 两种以连字符分隔的电话号码,一种三位区号、一种四位区号;

需要注意:使用分支条件时,要注意各个条件的顺序;

分析正则:\d{5}-\d{4}|\d{5},如果其条件顺序倒置会发生什么;

  • 如果倒置为\d{5}|\d{5}-\d{4},就是能匹配到5位数字(以及9位数字的前5位);
  • 匹配条件分支时,会从左到右测试每个条件,如果满足某个分支的话,就不会再去匹配其他条件了;

分组

重复单个字符时,需要在字符后加上限定符;要重复多个字符串,则需要使用小括号里指定子表达式(也叫分组);小括号在这里是元字符,表示分组

分析正则:(\d{1,3}\.){3}\d{1,3},这是一个简单的IP地址匹配表达式;

  • (\d{1,3}\.){3}匹配三位数字加上一个英文句号(整体作为一个分组)重复3次;
  • 最后再加上一个1到3位的数字;

但是他可能会匹配到不正确的IP地址,所以实际匹配IP地址会用到这个下面这个冗长的分组:
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

仔细分析下,现在我们已经能解读出这个正则表达式所匹配的含义了;


反义

如查找除xxx以外的任意字符,需要用到反义;

反义语法描述
\W匹配任意不是 字母数字下划线汉字的字符;
\S匹配任意不是 空白符的字符;
\D匹配任意非 数字的字符;
\B匹配不是单词开头或结束的位置;
[^x]匹配除了x以外的任意字符;
[^aeiou]匹配除了aeiou以外的任意字符;

反义示例:

  • \S+ 匹配 不包含空白字符的字符串;
  • <a[^>]+> 匹配 用尖括号括起来的以a开头的字符串;

其他进阶内容

  • 向后引用:通过组号/组名可以实现向后引用之前分组已经匹配的内容;
  • 零宽断言:零宽度正预测先行断言(?=exp),零宽度正回顾后发断言(?<=exp)
  • 负向零宽度断言:零宽度负预测先行断言(?!exp),零宽度负回顾后发断言(?<!exp)
  • 注释:(?#comment)
  • 贪婪与懒惰:表5.懒惰限定符;
  • 处理选项:IgnoreCase(忽略大小写) Multiline(多行模式) Singleline(单行模式) IgnorePatternWhitespace(忽略空白) ExplicitCapture(显式捕获);
  • 平衡组/递归匹配;
  • 其他内容:表7.尚未详细讨论的语法;

这些内容个人觉得相对高级,读了两遍有了一个大概的认识,后续有相关应用再予以深入。


应用实例:

[abc] a或b或c 
. 任意单个字符 
a? 零个或一个a
[^abc] 任意不是abc的字符 
\s 空格 
a* 零个或多个a
[a-z] a-z的任意字符 
\S 非空格 
a+ 一个或多个a
[a-zA-Z] a-z或A-Z 
\d 任意数字 
a{n} 正好出现n次a
^ 一行开头 
\D 任意非数字 
a{n,} 至少出现n次a
$ 一行末尾 
\w 任意字母数字或下划线 
a{n,m} 出现n-m次a
(...) 括号用于分组 
\W 任意非字母数字或下划线 
a*? 零个或多个a(非贪婪)
(a|b) a或b 
\b 单词边界 
(a)...\1 引用分组
(?=a) 前面有a 
(?!a) 前面没有a 
\B 非单词边界

import re

originstr = 'http://upos-sz-mirrorhw.bilivideo.com/upgcxcode/95/97/25aa6429795/256429795-1-32.flv?e=ig8euxZM2rNcNbN37zUVhoMgnwuBhwdEto8g5X10ugNcXBlqNxHxNEVE5XREto8KqJZHUa6m5J0SqE85tZvEuENvNo8g2ENvNo8i8o859r1qXg8xNEVE5XREto8GuFGv2U7SuxI72X6fTr859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IB_&uipk=5&nbs=1&deadline=1605519071&gen=playurl&os=hwbv&oi=737348678&trid=6dc40e32c716403881b67bf67f596472u&platform=pc&upsig=1a8747c29f3fbebd9754e8517eb2107c&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=0&orderid=0,3&agrr=1&logo=80000000'
# 匹配以/开头 以?结尾(由于两者是特殊标记 需要进行转义)的字符 且其中不包含/
bstr = r'\/([^\/]*)\?' 
b = re.compile(bstr ,  re.DOTALL)
str_list = b.findall(originstr)
print(str_list) 

按固定格式和位数提取字符串

bstr = r"""
  ^(?<recordType>[\w]{3,3})(?<senderType>[\w]{2,2})(?<senderId>\d{9,9})(?<senderName>[\x00-\xFF]{45,45})(?<ediStandard>[\x00-\xFF]{5,5})(?<creationDateTime>\d{14,14})(?<transmissionDate>\d{8,8})(?<characterSet>[\w\s]{0,15})"""

# 按固定格式提取字符串
b = re.compile(bstr ,  re.DOTALL)
str_list = b.match(content_str)

if str_list:
    linebits = str_list.groupdict()    
    for k, v in linebits.items() :
        # 去除解析之后的字符串的首尾空格
        linebits[k] = v.strip()
    return linebits

使用否定式前向查找提取字符串

import re

originstr = """
    <script>
        var liveUrl = "http://pull102.gzlz307.com/home/6d944f6ab72b3d069517146587a23c39/playlist.m3u8?only-audio=1";
        var liveId = "5190879905407162422";
        var liveStartTime = 1626761082249;
        var isSubscribed = !!0;
    </script>
"""

# 使用 否定式前向查找?! 来模拟逆向匹配:不包含某一字符串 的字符串 ((?!str).)*
bstr = r'liveUrl(((?!var).)*)var' 
b = re.compile(bstr ,  re.DOTALL)
str_list = b.findall(originstr)
print(str_list[0][0])

#  = "http://pull102.gzlz307.com/home/6d944f6ab72b3d069517146587a23c39/playlist.m3u8?only-audio=1";

对提取的日期进行判断是否是有效日期

import re

test_dates_str = """
19971-01-01
1960-00-00
1987-00-00
1981/05/20
1971-1-1
1971-1-0
"""

re_date_get = r'\d{4,5}[-/ ]\d{1,2}[-/ ]\d{1,2}'
re_date_normal = r'((^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(10|12|0?[13578])([-\/\._])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(11|0?[469])([-\/\._])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\d{2})|([2-9]\d{3}))([-\/\._])(0?2)([-\/\._])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([3579][26]00)([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][0][48])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][2468][048])([-\/\._])(0?2)([-\/\._])(29)$)|(^([1][89][13579][26])([-\/\._])(0?2)([-\/\._])(29)$)|(^([2-9][0-9][13579][26])([-\/\._])(0?2)([-\/\._])(29)$))'

test_dates = re.compile(re_date_get, re.DOTALL).findall(test_dates_str)

test_dates_dict = {}

for item in test_dates:
    s_item = re.compile(re_date_normal, re.DOTALL).search(item)
    if s_item:
        test_dates_dict[item] = s_item.group(0)
    else:
        test_dates_dict[item] = None
    
test_dates_dict

log:

{'19971-01-01': None,
 '1960-00-00': None,
 '1987-00-00': None,
 '1981/05/20': '1981/05/20',
 '1971-1-1': '1971-1-1',
 '1971-1-0': None}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值