正则表达式***(Regular Experssion)

正则表达式

正则表达式缩写为regex、regexp、RE等。他是文本处理极为重要的技术,其应用非常广泛,shell中处理文本的命令、各种高级编程语言都支持正则表达式,用它可以对字符串按照某种规则进行检索和替换,

分类

  • BRE:基本正则,grep、sed、vi等软件支持。vim支持扩展正则
  • ERE:扩展正则,egrep(grep -E)、sed -r等。
  • PCRE:几乎所有高级语言都是PCRE的变种。Python的RE可以认为是PCRE的子集

基本语法

  • 元字符(matchcharacter)
代码 说明 举例
. 匹配除换行符外任意一个字符
[abc] 字符集合,只能表示一个字符位置。匹配所包含的任意一个字符 [abc]匹配plain中的’a’
[^abc] 字符集合,只能表示一个字符位置。匹配出去集合内字符的任意一个字符 [^abc]可以匹配plain中p、l、i、n
[a-z] 字符范围,也是集合,只能表示一个字符位置。匹配集合所包含的任意一个字符
[^a-z] 字符范围,也是集合,只能表示一个字符位置。匹配除集合内字符的任意一个字符
\b 匹配单词边界 \bb在文本中找到单词中b开头的b字符
\B 不匹配单词的边界 t\b包含t的单词但是不以t结尾的t字符,例如write;\Bb不以b开头的含有B的单词,例如table
\d [0-9]匹配1位数字
\D [^0-9]匹配一位非数字
\s 匹配1位空白字符,包含换行符、制表符、空格
\S 匹配一位非空白字符
\w 匹配[a-zA-Z0-9_],包括中文的字
\W 匹配\w之外的字符
  • 单行模式:
    .可以匹配所有字符,包括换行符
    ^表示整个字符串开头,$表示整个字符串的结尾
  • 多行模式:
    .可匹配除换行符之外的字符
    ^表示行收,$表示行尾
    ^表示整个字符串的开始,$表示整个字符串的结尾。开始指的是\n后紧接着的下一个字符,结束指的是\n前的字符

注意:字符串中看不见的换行符,windows中的回车换行符\r\n会影响$的测试。

  • 转义:凡是在正则表达式中有特殊意义的符号,如果想使用它的本意,请使用\转义。反斜杠自身,需使用\\;\r、\n是转义后代表回车、换行
  • 重复
符号 说明 举例
* 表示前面的正则表达式会重复0次或多次 e\w*单词e后面有0到任意个非空字符
+ 表示前面的正则表达式重复至少一次 e\w+单词e后至少有一个非空白字符
? 表示前面的正则表达式会重复0或1次 e\w?单词e后最多有一个非空白字符
{n} 重复固定的n次 e\w{1}单词e后面只能有一个非空白字符
{n,} 重复至少n次 e\w{1,}等价e\w+;\w{0,}等价e\w*;e\w{0,1}等价e\w?
{n,m} 重复至少n次,最多m次 e\w{1,10}单词e后面至少1个,最多是个非空白字符
  • 练习
    • 匹配手机号码:\d{11}
    • 匹配固定电话号码\d{3-4}-\d{7-8}
  • 分组
代码 说明
x|y 匹配x或y
(pattern) 使用小括号制定一个子表达式,也叫分组。捕获后会自动分配组号从1开始,可以改变优先级
\数字 匹配对应分组
(?:pattern) 如果仅仅为了改变优先级,就不需要捕获分组
(?<name>exp)(?’name’exp) 分组捕获,但是可以通过name访问分组
零宽断言
(?=exp) 零宽度正预测先行断言:断言exp一定在匹配的右边出现,也就是说断言后面一定跟个exp
(?<=exp) 零宽度正回顾:后发断言:断言exp一定出现在匹配的左边,业绩是前面一定有个exp前缀
负向零宽断言
(?!exp) 零宽度付预测先行断言:断言一定不会在右侧
(?<!exp) 零宽度负回顾后发断言:断言一定不能出现在左侧
注释
(?#comment) 注释

总结:分组和捕获是同一个意思;同时在正则中能用简单表达式就不要用复杂的

贪婪与非贪婪

正则表达杀死默认是贪婪模式,尽量多匹配更长的字符串。非贪婪模式很简单就是再重复的符号后加一个”?”,匹配最少的字符串。

代码 说明
*? 匹配人一次,尽量少重复
+? 匹配至少一次,少重复
?? 匹配0或依次,少重复
{n,}? 匹配至少n次,少重复
{n,m}? 匹配n到m次,,少重复
  • 引擎选项
代码 说明 Python
IgnoreCase 匹配时忽略大小写 re.I,re.IGNORECASE
Singleline 单行模式:.可以匹配所有字符,包括\n re.S,re.DOTALL
Multiline 多行模式:^行首,$行尾 re.M,re.MULTILINE
IgnorePatternWhitespace 忽略表达式中的空白字符,如果要使用空白字符用转义,#可以用来做注释 re.X,re.VERBOSE

练习

  • 匹配0-999之间的数字 #匹配两位数 [1]?\d #匹配三位数 ^([1-9]\d\d?|\d)(?!\w+) ^([1-9])\d\d?|\d)\r?$
  • 匹配点分四段式IP
    关于IP地址验证问题:
    - 可以把数据提取出来后交给IP地址解析库(socket模块)处理,解析异常就说明IP有问题,如果正常则表示Ip合法
    
    import socket
    nw = socket.inet_aton('1.2.4.8')    # 输入IP字符串,返回bytes,如果抛出异常则表示IP不合法
    print(nw,socket.inet_ntoa(nw))  # inet_ntoa是反向解析
    
    - 可以使用复杂正则表达式验证地址正确性
    
    - 前导0是可以的

    (?:(25[0-5]|2[0-4]\d|[01]?\d\d?).){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)

  • 选出有Ftp的连接,且文件类型是gz或者xz的文件名
    ftp://ftp.astron.com/pub/file/file-5.14.tar.gzftp://ftp.gmplib.org/pub/gmp-5.1.2/gmp-5.1.2.tar.xzftp://ftp.vim.org/pub/vim/unix/vim-7.3.tar.bz2http://anduin.linuxfromscratch.org/sources/LFS/lfs-packages/conglomeration//iana-etc/iana-etc-2.30.tar.bz2http://anduin.linuxfromscratch.org/sources/other/udev-lfs-205-1.tar.bz2http://download.savannah.gnu.org/releases/libpipeline/libpipeline-1.2.4.tar.gzhttp://download.savannah.gnu.org/releases/man-db/man-db-2.6.5.tar.xzhttp://download.savannah.gnu.org/releases/sysvinit/sysvinit-2.88dsf.tar.bz2http://ftp.altlinux.org/pub/people/legion/kbd/kbd-1.15.5.tar.gzhttp://mirror.hust.edu.cn/gnu/autoconf/autoconf-2.69.tar.xzhttp://mirror.hust.edu.cn/gnu/automake/automake-1.14.tar.xz

    <?<=.*ftp.*/>[^/]\.(?:gz|xz)

Python的re

Python使用re模块提供了正则的处理能力

常量

  • re.M = re.MULTILINE -> 多行模式
  • re.S = re.DOTALL -> 单行模式
  • re.I = re.IGNORECASE -> 忽略大小写
  • re.X = re.VERBOSE -> 忽略表达式中的空白字符

使用‘|’位或运算符开启多种选项

方法

  • 编译
    • re.compile(pattern,flags=0)
      1. 设定flags,编译模式,返回正则表达式对象
      2. pattern是正则表达式字符串,flags是选项。正则表达式需要被编译,为了提高效率,这些便宜的结果被保存,下次使用就不需要再次编译。re的其他方法为提高效率都调用了编译方法。
  • 单次匹配
    1. re.match(pattern,string,flags=0)/regex.match(string[,pos[,endpos]])match匹配从字符串的开头匹配,regex对象match方法可以设定开始位置和结束位置。返回match对象
    2. re.search(pattern,string,flags=0)/regex.search(string[,pos[,edpos]])从头搜索知道第一个匹配,regex对象search方法可以设定开始位置和结束位置,返回match对象
    3. re.fullmatch(pattern,string,flags=0)/regex.match(string[,pos[,endpos]])这个字符串和正则表达式匹配,要完全匹配,多了或少了都不行
    4. 以上三种方法返回的都是match对象
  • 全部匹配
    • re.findall(pattern,string,flags=0)等价于regex.findall(string[,pos[,endpos]])对整个字符串从左到右匹配,返回所有匹配项的列表
    • re.finditer(pattern,string,flags=0)等价于regex.finditer(string[,pos[,endpos]])对整个字符串从左向右匹配,返回所有匹配项的迭代器。每次迭代返回的是match对象
  • 匹配替换
    • re.sub(pattern,replacement,string,count=0,flags=0)使用pattern对字符串string进行匹配,对匹配项使用repl替换。repl可以是string、bytes、function
    • subn(pattern,replacement,string,count=0,flags=0)返回一个元组(new_string,number_of__subs_made)
  • 分割字符串
    • 字符串分割函数不能指定多个字符进行分割,不好用。
    • re.split(pattern,string,maxsplit=0,flags=0)maxsplit指最大分割数,默认是全部分割
    import re
    
    s = '''01 bottle
    02 bag
    03      big1
    100       able'''
    
    print(s.split())    #做不到
    ret = re.split('[\s\d]+',s)
    print(1,ret)
    regex = re.compile('^[\s\d]+')
    ret = regex.split(s)
    print(2,ret)
    regex = re.compile('^[\s\d]+',re.M)
    ret = regex.split(s)
    print(3,ret)
    
    regex = re.compile('\s+\d+\s+')
    ret = regex.split(' ' + s)
    print(4,ret)
  • 分组使用小括号的pattern捕获的数据被放到了组group中。
    • match、search函数可以返回match对象;findall返回字符串列表;finditer返回一个个match对象
    • 如果pattern中使用了分组,如果有匹配的结果,会在match对象中:使用group(N)方式返回对应分组,1-n是对应的分组,0返回整个匹配的字符串;如果使用了命名分组,可以使用group(‘name’)的方式取分组;也可以使用groups()返回所有命名的分组;使用groupdict()返回所有命名的分组组成的字典

练习

  • 匹配邮箱地址
    test@hot-mail.com
    v-ip@magedu.com
    web.manager@magedu.com
    super.user@gmail.com
    a@w-a-com
  • 匹配html标记内的内容
    <a href='http://www.baidu.com/index.html' target='_blank'>马哥教育</a>
  • 匹配url
    http://www.baidu.com/index.html
    https://login.magedu.com
    file:///etx/syconf/newksa
  • 匹配二代身份证ID
  • 匹配密码强弱
  • 单词统计
1.

[\w-]+@[^\s-]+.com

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

2.
<a href='(?<url>[^']+)' [^<>]+>\w+</a>

html提取
<[^<>]+>(.*)<[^<>]+>

匹配标记a
<(\w+)\s+{^<>}+>(.*)(</\1>)

3.
url提取
(\w+)://([^\s]+)

4.
\d{17}[0-9xX]|\d{15}

5.
^[a-zA-Z0-9_]{10,15}$

6. 单词统计

import re
from collections import defaultdict

regex=re.compile(r'[^\w-]')

d = defaultdict(lambda : 0)
with open('d:/sample.txt',encoding='utf-8') as f:
    for line in f:
        for x in regex.split(line):
            if len(x) >= 1:
                d[x.lower()] += 1

# print(d)
print(sorted(d.items(),key= lambda x:x[1],reverse=True))
print(d['path1'],d['sub-path'])

def word_cnt(path:str):
    d = defaultdict(lambda :0)
    with open('d:/sample.txt',encoding='utf-8') as f:
        for line in f:
            for x in regex.split(line):
                if len(x) >= 1:
                    d[x.lower()] += 1
    return d
#print(word_cnt('d:/sample.txt'))

i = 0
for x in sorted(word_cnt('d:/sample.txt').items(),key = lambda x:x[1],reverse=True):
    if i < 10:
        print(x)
    i += 1
print(word_cnt('d:/sample.txt')['path1'],word_cnt('d:/sample.txt')['sub-path'])

  1. 1-9

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88237

(0)
KX_ilKX_il
上一篇 2017-11-05
下一篇 2017-11-05

相关推荐

  • 第十一周作业

    1、详细描述一次加密通讯的过程,结合图示最佳。 一次通信加密过程: 发送方: 1.使用单向加密算法提取生成数据特征码; 2.使用自己的私钥加密特征码附加在数据后面; 3.生成用于对称加密的临时密钥; 4.用此临时密钥加密数据和已经使用私钥加密后的特征码; 5.使用接受方的公钥加密此临时密钥,附加在对称密钥后的数据后方; 接受方: 1.使用自己的私钥解密加密的…

    2017-10-02
  • bash特性之命令行展开功能应用示例

                       bash特性之命令行展开功能应用示例 1.创建/tmp的目录下:a_c,a_d,b_c,b_d 创建命令:mkdir -pv /tmp/{a,…

    Linux干货 2017-07-09
  • 当Web访问性能出现问题,如何深探?

    对运维或开发工程师来说,遇到访问性能问题时,最先需要定位的是问题出现在哪个环节,是网络的问题,服务端的问题,还是客户端的问题? 往往技术人员喜欢把精力放在保障后端服务的可用性方面,而对前端界面是否能正常装载,是否能完整渲染不是太关心。但从业务的角度来说,界面承载的才是最终的业务,业务是通过人机交互来实现的。 日常我们遇到哪些场景需要定位访问性能瓶颈? ·不同…

    系统运维 2017-01-09
  • bash特性及bash脚本编程初步

    bash特性之命令hash 之前我们讲过用户在执行一个命令的时候bash会遍历环境变量$PATH中所有路径来查找执行文件。而命令hash是用来缓存之前用户使用过的命令下次执行的时候直接搜索hash缓存来减少对$PATH变量中路径的遍历次数,从而提高系统运行效率 hash:hash命令     hash:列出 &nbsp…

    Linux干货 2016-12-20
  • Linux如何快速查找需要执行的命令

    大家知道,对于熟悉命令行的用户来说,命令行操作比图形界面操作高效、简洁,那么Linux是如何快速找到命令的位置呢?今天刚学了这一部分,与大家分享。         Linux将命令分为内部命令和外部命令,这是由于一些命令是常用的,需要常驻内存以减少检索时间,所以集成在shell之…

    2017-07-15
  • Linux 学习基本

    环境配置主要分硬件和软件两种

    2018-03-26