【Python之路】特别篇--Python正则表达式

正则表达式的基础

  正则表达式并不是Python的一部分。

  正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。

  得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同。

  就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。

 

元字符

元字符: . ^ $ * + ? { [ ] \ | ( )

. 匹配除了换行符外所有字符 (通配符)

content = 'Abcdefghijklmnopq'
test = re.findall(r"b.d",content)
print(test)
['bcd']

^ 以....开头

content = 'Abcdefghijklmnopq'
test = re.findall(r"^Abcd",content)
print(test)
['Abcd']

$ 以....结尾

content = 'Abcdefghijklmnopq'
test = re.findall(r"nopq$",content)
print(test)
['nopq']

* 匹配0到多次 {0, }  控制它前面的字符 

content = 'Abcdefghijklmnopq'
test = re.findall(r"A.*e",content)
print(test)
['Abcde']

+ 匹配1到多次 {1, }

content = 'abcdefab111111'
test = re.findall(r"ab1+",content)
print(test)
['ab111111']

?  匹配0到1次 {0,1}

content = 'abcdefab111111'
test = re.findall(r"ab1?",content)
print(test)
['ab', 'ab1']

* +  都是按照贪婪模式进行匹配  非贪婪模式 需要在后面加个?

content = 'abcdefab111111'
test = re.findall(r"ab1+?",content)
print(test)
['ab1']

re.search(r"a(\d+?)","a2345").group() => a2

re.search(r"a(\d*?)","a2345").group() => a
 
#如果前后均有限定条件 ?不起作用 re.search(r"a(\d*?)b","a2345b").group() => a2345b

( ) 组  作为一个整体

content = 'abcdefab111111'
test = re.findall(r"(ab1)",content)
print(test)
['ab1']

{ }  重复次数自定义

content = 'abcdefab111111'
test = re.findall(r"ab1{3,9}",content)
print(test)
['ab111111']

[ ]  字符集  表示或

      字符集里面元字符会失去意义   除了 -  \  ^ 3个元字符外

content1 = 'wwwwwabdxxxxx'
test1 = re.findall(r"a[bc]d",content1)
print(test1)
#['abd']

content2 = 'wwwwwacdxxxxx'
test2 = re.findall(r"a[bc]d",content2)
print(test2)
#['acd']

***********************************************************************

content = 'wwwwwa.xxxxx'
test = re.findall(r"a[.]x",content)
print(test)
#['a.x']

content = 'wwwww1234xxxxx'
test = re.findall(r"[1-9]",content)       #1~9的数字
print(test)
#['1', '2', '3', '4']

content = 'wwwww1234xxxxx'
test = re.findall(r"[^1-9]",content)       #非1~9的数字
print(test)
#['w', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', 'x']

\ 作用:

  • 后面跟元字符去除特殊功能
  • 后面跟普通字符实现特殊功能
  • 引用序号对应的字组所匹配的字符串
test = re.search(r"(alex)(eric)com\2","alexericcomeric")
print(test.group())
#alexericcomeric

\d  匹配任何十进制数, [0-9]

\D  匹配任何非数字字符 [^0-9]

\s   匹配任何空白字符 [ \t\n\r\f\v ]

\S  匹配任何非空白字符 [^ \t\n\r\f\v ]

\w  匹配任何字母数字字符 [a-zA-Z0-9_]

\W 匹配任何非字母数字字符 [^a-zA-Z0-9]

\b  匹配一个单词边界,单词和空格间的位置  匹配特殊字符(不单止空格)

content = 'wwwww1234xxxxx'
test = re.findall(r"\d",content)
print(test)
# ['1', '2', '3', '4']

content = 'ww&*#$%ww1234xx'
test = re.findall(r"\D",content)
print(test)
# ['w', 'w', '&', '*', '#', '$', '%', 'w', 'w', 'x', 'x']

content = 'asdasd   '
test = re.findall(r"\s",content)
print(test)
# [' ', ' ', ' ']

content = ' asdasd   '
test = re.findall(r"\S",content)
print(test)
# ['a', 's', 'd', 'a', 's', 'd']

content = 'abc123^&*lm-\_'
test = re.findall(r"\w",content)
print(test)
# ['a', 'b', 'c', '1', '2', '3', 'l', 'm', '_']

content = 'abc123^&*lm-\_'
test = re.findall(r"\W",content)
print(test)
# ['^', '&', '*', '-', '\\']

content = 'I like Sooooo'
test = re.findall(r"like\b",content)
print(test)
# ['like']

*******************************************
test = re.findall(r"abc\b","asdasd abc ")
test = re.findall(r"abc\b","asdasd abc*")
print(test)
# ['abc']

 

match()

  match()

# match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
  
match(pattern, string, flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# falgs  : 匹配模式
 
# re.I(re.IGNORECASE):    忽略大小写(括号内是完整写法,下同)
# M(MULTILINE):     多行模式,改变'^'和'$'的行为
# S(DOTALL):     点任意匹配模式,改变'.'的行为   使 . 匹配包括换行在内的所有字符
# L(LOCALE):     使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
# U(UNICODE):  使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
# X(VERBOSE):   详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:

*************************************

# match对象的方法

.group()	获取匹配到的所有结果
.groups()	获取模型中匹配到的分组结果	
.groupdict()	获取模型中匹配到的分组中所有执行了key的组
.group()       返回被RE匹配的字符串 可以加参数group(1) 组号
.start()        返回匹配开始的位置
.end()       返回匹配结束的位置
.span()         返回一个元组包含的匹配的位置
# 无分组
r = re.match("h\w+", origin)
print(r.group())     # 获取匹配到的所有结果
print(r.groups())    # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组结果

# 有分组

# 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)

r = re.match("h(\w+).*(?P<name>\d)$", origin)
print(r.group())     # 获取匹配到的所有结果
print(r.groups())    # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
demo

search()

  search()

# search 匹配成功有结果,返回match对象
# 查看返回结果用.group()
# search,浏览整个字符串去匹配第一个,未匹配成功返回None
# search(pattern, string, flags=0)
# 无分组

r = re.search("a\w+", origin)
print(r.group())     # 获取匹配到的所有结果
print(r.groups())    # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组结果

# 有分组

r = re.search("a(\w+).*(?P<name>\d)$", origin)
print(r.group())     # 获取匹配到的所有结果
print(r.groups())    # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
demo

findall()

  findall()

# 优先取组里内容返回!
# findall,获取非重复的匹配列表;如果有一个组则以列表形式返回,且每一个匹配均是字符串;如果模型中有多个组,则以列表形式返回,且每一个匹配均是元祖;
# 空的匹配也会包含在结果中
# findall(pattern, string, flags=0)
 
data = re.findall("\d+\w\d+",'a2b3c4d5')
# ['2b3', '4d5']
# re.findall()	匹配成功一个后,从匹配成功最后位置开始下一次查找

# 空的匹配也会包含在结果中
data = re.findall("",'a2')
print(data)
# ['', '', '']

***********************************

#有几个括号就取几次

data = re.findall(r'(\dasd)*','1asd2asdp3asd3434')
print(data)
# ['2asd', '', '3asd', '', '', '', '', '']
# 贪婪匹配 第一段取到1asd2asd 但最后返回 2asd  取最后一个!

如下:
a= "alex"
data = re.findall(r'(\w)(\w)(\w)(\w)',a)
print(data)
# [('a', 'l', 'e', 'x')]

data = re.findall(r'(\w){4}',a)
print(data)
# ['x']    => 只是执行了4次,返回还是按一个括号算,取最后匹配的一项

***********************************

test = re.findall("www.(baidu|laonanhai).com","asdsa www.baidu.com")
print(test)
# ['baidu']
 
添加 ?: 去掉优先权
test = re.findall("www.(?:baidu|laonanhai).com","asdsa www.baidu.com")
print(test)
# ['www.baidu.com']
# 无分组
r = re.findall("a\w+",origin)
print(r)

# 有分组
origin = "hello alex bcd abcd lge acd 19"
r = re.findall("a((\w*)c)(d)", origin)
print(r)
# [('bc', 'b', 'd'), ('c', '', 'd')]
demo

sub()

  sub()

# sub,替换匹配成功的指定位置字符串
 
sub(pattern, repl, string, count=0, flags=0)
# pattern: 正则模型
# repl   : 要替换的字符串或可执行对象
# string : 要匹配的字符串
# count  : 指定匹配个数
# flags  : 匹配模式

test = re.sub("g.t","have","I get A, I got B , I gut C")
print(test)
#I have A, I have B , I have C

********************************************

#subn    最后还返回一个替换次数

origin = "ale4 xc 19"
data,counts = re.subn("\d+","KKK",origin)
print(data,counts)
# aleKKK xc KKK 2

compile()

  compile()

regex = re.compile(r"\w*oo\w*")
text = " JGood is ,he is cool"
data = regex.findall(text)
print(data)
#['JGood', 'cool']

split()

  split()

# split,根据正则匹配分割字符串
split(pattern, string, maxsplit=0, flags=0)

# pattern: 正则模型
# string : 要匹配的字符串
# maxsplit:指定分割个数
# flags  : 匹配模式

*****************************************
# 有分组情况下, 把分割的项也添加进去

origin = "hello alex bcd alex lge alex acd 19"
r1 = re.split("(alex)", origin, 1)
print(r1)
# ['hello ', 'alex', ' bcd alex lge alex acd 19']

r2 = re.split("(al(ex))", origin, 1)
print(r2)
# ['hello ', 'alex', 'ex', ' bcd alex lge alex acd 19']

*****************************************

p = re.compile(r"\d+")
test = p.split("one1two2three3four4")
print(test)
# ['one', 'two', 'three', 'four', '']
# 末尾有空字符串
 
=> one,two2three3four4 => ['one']  two,three3four4 => ..
 
test = re.split('[bc]','abcd')
print(test)
# ['a', '', 'd']
# 无分组
origin = "hello alex bcd alex lge alex acd 19"
r = re.split("alex", origin, 1)
print(r)

# 有分组
        
origin = "hello alex bcd alex lge alex acd 19"
r1 = re.split("(alex)", origin, 1)
print(r1)
r2 = re.split("(al(ex))", origin, 1)
print(r2)
demo

finditer()

  finditer()

# 返回结果为迭代对象

p = re.compile(r"\d+")
w = p.finditer(' 1 drum44ers druming , 11 ... 10 ...')
for match in w:
    print(match.group(),match.span())

# 1 (1, 2)
# 44 (7, 9)
# 11 (23, 25)
# 10 (30, 32)

 

反斜杠的困扰

与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。

假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\"

前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。

Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。

同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。

 

常用正则表达式

# IP:
^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$

# 手机号:
^1[3|4|5|8][0-9]\d{8}$

# 邮箱:
[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+

 

练习题:计算器

#!/usr/bin/env python
# -*-coding:utf-8 -*-

import re

# 加减运算:
def add_sub(origin):
    '''
    进行加减运算,按+ - 符号拆分,
    :param origin: 传入的表达式
    :return:
    '''
    data = re.split('([+-]{1})', origin)
    # 预处理 把 '' 转换成 '0'
    for i in range(len(data)):
        if data[i].strip() == '':
            data[i] = '0'
    # 预处理 负负得正 情况:
    for i in range(len(data)):
        if data[i].strip() == '0' and i != 0 :
            if data[i-1].strip() == '-':
                data[i+1] = '+'

    # 计算开始:
    total = 0
    # flag = 0 默认加号  1为减号
    flag = 0
    for i in range(len(data)):
        if data[i].strip() == '-':
            flag = 1
            continue
        elif data[i].strip() == '+':
            flag = 0
            continue
        elif flag == 0 :
            total += float(data[i].strip()) if '.'in data[i] else int(data[i].strip())
        elif flag == 1 :
            total -= float(data[i].strip()) if '.'in data[i] else int(data[i].strip())

    return total


# 计算乘除,乘方运算:
def plus_div(origin):
    '''
    按前后顺序拆分,需要进行 乘除,乘方 运算的地方,获得式子,计算出结果,顶替回原来位置,循环此步骤,只剩加减法,再把只剩加减法的式子传入add_sub()计算
    :param origin: 传入的表达式
    :return:
    '''
    while True:
        data = re.split('([-]?\d+\.?\d*\s*(?:[*/]|\*\*)\s*[-]?\d+\.?\d*)',origin,1)
        # 判断表达式是否只剩加减运算 , 传入 add_sub() 进行加减法的计算
        if len(data) ==1:
            result = add_sub(data[0])
            return result
            break
        # 判断表达式是否还包含 乘除,乘方 符号 ,如果包含,继续拆分运算,最终获得只剩加减的式子
        elif len(data) ==3:
            counts = data[1]    # 获得匹配出的 运算符号 * / 或 **
            before, content, after = data
        else:
            print('Error')

        # 判断需要进行哪种运算,
        if '**' in counts:
            num = counts.split('**')
            left = float(num[0].strip()) if '.'in num[0] else int(num[0].strip())
            right = float(num[1].strip()) if '.' in num[1] else int(num[1].strip())
            total = left ** right

        elif '/' in counts:
            num = counts.split('/')
            left = float(num[0].strip()) if '.'in num[0] else int(num[0].strip())
            right = float(num[1].strip()) if '.' in num[1] else int(num[1].strip())
            total = left / right

        elif '*' in counts:
            num = counts.split('*')
            left = float(num[0].strip()) if '.'in num[0] else int(num[0].strip())
            right = float(num[1].strip()) if '.' in num[1] else int(num[1].strip())
            total = left * right

        # 此时只进行了表达式内的一次乘除,乘方运算, 计算结果拼接顶替原来的位置,继续循环直到只剩加减法
        origin = before + str(total) + after


def count(origin):
    """
    1.一步一步拆分括号,每获得一次 最里层的括号表达式 ,就传入plus_div()函数 进行 该表达式的所有乘除运算,再进行加减运算,返回该表达式最终结果,
    拼接回原表达式,替换回括号位置.再循环寻找下一个括号内表达式,直到所有括号的表达式计算完毕,最终只剩一条只包含单纯的乘除加减运算的表达式.

    :param origin: 需要计算的表达式
    :return:
    """
    while True:
        # 拆分() 获得最里层的括号!
        data = re.split('\(([^()]+)\)',origin,1)
        if len(data) == 3:
            before,content,after = data
            result = plus_div(content)      # () 里面的表达式传入 plus_div 进行计算
            origin = before + str(result) + after   # 把计算结果 拼接回原来括号的位置. 进行下一次括号寻找!
        else:
            # 当只剩一条只包含单纯的乘除加减运算的表达式时,
            result = plus_div(data[0])
            return result
            break


origin = " 6 * 2 + ( 4 -3.5 + 1 * 91 + (-1 +2) * 5 -5 ) / 2 + 2 ** 9"
result = count(origin)


print('表达式: ' + origin)
print('我的计算结果 :\n' + str(result))
print('eval计算结果: \n'+ str(eval(origin)))
View Code

 

posted @ 2016-11-26 10:44  5_FireFly  阅读(1817)  评论(0编辑  收藏  举报
web
counter