正则表达式***(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)
上一篇 2017-11-05 16:35
下一篇 2017-11-05 23:38

相关推荐

  • Linux简单的磁盘分区、格式化和挂载

     一.分区: 1.查看分区fdisk -l 通过fdisk -l 查看机器所挂硬盘个数及分区情况   2.通过上面的信息,我们知道此机器中挂载两个硬盘分别是:/dev/sda和/dev/sdb, 其中sda有两个主分区/dev/sda1和sda2; 其中sdb有1个扩展类型/dev/sdb3和3个逻辑分区sd…

    Linux干货 2016-02-28
  • linux目录分类和各个目录作用-2016-07-21

    linux目录分类和各个目录作用-2016-07-21 Linux安装以后出现的文件目录的作用:  文件系统的类型  LINUX有四种基本文件系统类型:普通文件、目录文件、连接文件和特殊文件,可用file命令来识别。  普通文件:如文本文件、C语言元代码、SHELL脚本、二进制的可执行文件等,可用cat、less、more、vi…

    Linux干货 2016-08-04
  • vim 的使用简介

    linux day 8 使用vi和vim的三种主要模式 移动光标,进入插入模式 改变、删除、复制文本 撤销改变 搜索文档 vim寄存器 可视化和多窗口 vim帮助 vi: Visual Interface,文本编辑器 文本:ASCII, Unicode 文本编辑种类: 行编辑器: sed 全屏编辑器:nano, vi vim-Vi Improved 其他编辑…

    Linux干货 2016-08-15
  • cp复制软链接失效的原因

    在学习cp命令时我们会知道复制软链接时,如果要保留链接文件使用-d,但当我们实际操作时却常常出现如下情况 [root@localhost ~]# ls -l /etc/redhat-release     #此文件为链接文件 lrwxrwxrwx. …

    Linux干货 2016-10-25
  • 马哥教育网络20期+第四周课程练习

    1、复制/etc/skel目录为/home/tuser1,要求/home/tuser1及其内部文件的属组和其它用户均没有任何访问权限。 ~]# cp -r /etc/skel /home/tuser1 ~]# chmod -R go-r /home/tuser1 2、编辑/etc…

    Linux干货 2016-07-04
  • AWK——文本处理三剑客

    GNU awk ·文本处理三工具:grep,sed,awk          grep,egrep,fgrep:文本过滤工具:pattern          sed:行编辑器  &nbsp…

    Linux干货 2016-09-20