正则表达式***(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

相关推荐

  • 设计模式(五)适配器模式Adapter(结构型)

    1. 概述:          接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题。程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化。         例子1:iphone…

    Linux干货 2015-06-25
  • 马哥教育网络班22期+第5周课程练习 忍者乱太郎喻成

    1、显示当前系统上root、fedora或user1用户的默认shell;   以root为例 awk -F':' '$1 ~ /root/ {print $7} ' /etc/passwd 2、找出/etc/rc.d/…

    Linux干货 2016-10-09
  • 关于LNMP架构的网站迁移的事(第一版)

    大家好: 今天分享下在生产环境中如何对LNMP架构的两台服务器群的网站进行迁移及其操作思路: 1– 首先要对老服务器上的nginx,PHP, mysql的版本及其安装方式要彻底了解。 特别对php来说,用php -m 来了解老服务器上php所安装过的插件。 [azureuser@cnux17 ~]$ php -m [PHP Modules] ap…

    Linux干货 2016-12-26
  • N25-Bazinga-第四周作业

    N25-Bazinga-第四周作业 1.复制/etc/ske1目录为/home/tuser1,要求/home/tuser及其内部文件的属组和其他用户均没有任何访问权限。 [root@localhost ~]# cp -a /etc/skel/ /home/tuser1 [root@localhost&nbsp…

    Linux干货 2016-12-21
  • 26期全程班-第二周博客作业

    1、Linux上的文件管理类命令都有哪些,其常用的使用方法及其相关示例演示。     cp  复制命         ex:cp -a /etc/initab /var/log/message /tmp/   …

    Linux干货 2017-01-10
  • Linux 文件管理常用基本命令

    马哥教育网络班21期+第二周练习 目录管理 ls (list):列出目录及内容 ls [OPTION]… [FILE]…     -l:长格式显示文件的详细属性信息         -rwxr-xr-x.&nbs…

    Linux干货 2016-07-17