Python学习——正则表达式与re模块实现字符串计算器

学习了python的正则表达式后,一片懵逼,不知道干啥用的,也不知道咋用,只能放一些实例和正则表达式的规则以备后续查阅,希望在经过长时间的训练和使用后能对正则表达式有一个深刻的理解。什么是正则表达式呢?借用林海峰老师的话说就是:描述一类事物的规则。在Python中就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法,并通过 re 模块实现。

先介绍正则表示一些常用的匹配模式,以及结合re模块的使用方法,然后写一个基于正则表达式、字符串的计算器

正则表达式常用的匹配模式如下表:

匹配模式

描述

\w

匹配字母、数字、下划线

\W

匹配非(字母、数字、下划线)

\s

匹配任意空白字符(看不见的字符),

\S

匹配任意非空字符

\d

匹配任意数字,等价于[0-9]

\D

匹配任意非数字

\A

匹配字符串开始

\Z

匹配字符串结束,如果存在换行,则只匹配到换行前的结束字符

\z

匹配字符串结束

\G

匹配最后完成匹配的位置

\n

匹配换行符

\t

匹配制表符

^

匹配字符串的开头

$

匹配字符串的末尾

.

匹配任意字符,除去换行符,当re.DOTALL标记被指定时,则可以匹配任意字符

[…]

匹配[]中一组字符其中一个即可

[^…]

匹配不在[]中的一组字符

*

匹配0个或多个表达式

*?

惰性匹配上一个

+

匹配1个或多个表达式

+?

惰性匹配上一个

?

匹配0个或1个由前面的正表达式定的片段,非贪婪方式

{n}

精确匹配n个前面的表达式

{n,m}

匹配n到m个由前面的正表达式定的片段,贪婪方式

a|b

匹配a或者b

()

匹配()内的表达式,表示一个组

{n,}

匹配前一个字符或者子表达式至少n

{n,}?

前一个的惰性匹配

[\b]

退格字符

下面就来详细一个分析吧:

以下举例部分摘自林海峰博客

http://www.cnblogs.com/linhaifeng/articles/6384466.html#_label13

 
import re
#\w与\W
print(re.findall('\w','hello egon 123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']
print(re.findall('\W','hello egon 123')) #[' ', ' ']

#\s与\S
print(re.findall('\s','hello  egon  123')) #[' ', ' ', ' ', ' ']
print(re.findall('\S','hello  egon  123')) #['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']

#\n \t都是空,都可以被\s匹配
print(re.findall('\s','hello \n egon \t 123')) #[' ', '\n', ' ', ' ', '\t', ' ']

#\n与\t
print(re.findall(r'\n','hello egon \n123')) #['\n']
print(re.findall(r'\t','hello egon\t123')) #['\n']

#\d与\D
print(re.findall('\d','hello egon 123')) #['1', '2', '3']
print(re.findall('\D','hello egon 123')) #['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' ']

#\A与\Z
print(re.findall('\Ahe','hello egon 123')) #['he'],\A==>^
print(re.findall('123\Z','hello egon 123')) #['he'],\Z==>$

#^与$
print(re.findall('^h','hello egon 123')) #['h']
print(re.findall('3$','hello egon 123')) #['3']

# 重复匹配:| . | * | ? | .* | .*? | + | {n,m} |
#.
print(re.findall('a.b','a1b')) #['a1b']
print(re.findall('a.b','a1b a*b a b aaab')) #['a1b', 'a*b', 'a b', 'aab']
print(re.findall('a.b','a\nb')) #[]
print(re.findall('a.b','a\nb',re.S)) #['a\nb']
print(re.findall('a.b','a\nb',re.DOTALL)) #['a\nb']同上一条意思一样

#*
print(re.findall('ab*','bbbbbbb')) #[]
print(re.findall('ab*','a')) #['a']
print(re.findall('ab*','abbbb')) #['abbbb']

#?
print(re.findall('ab?','a')) #['a']
print(re.findall('ab?','abbb')) #['ab']
#匹配所有包含小数在内的数字
print(re.findall('\d+\.?\d*',"asdfasdf123as1.13dfa12adsf1asdf3")) #['123', '1.13', '12', '1', '3']

#.*默认为贪婪匹配
print(re.findall('a.*b','a1b22222222b')) #['a1b22222222b']

#.*?为非贪婪匹配:推荐使用
print(re.findall('a.*?b','a1b22222222b')) #['a1b']

#+
print(re.findall('ab+','a')) #[]
print(re.findall('ab+','abbb')) #['abbb']

#{n,m}
print(re.findall('ab{2}','abbb')) #['abb']
print(re.findall('ab{2,4}','abbb')) #['abb']
print(re.findall('ab{1,}','abbb')) #'ab{1,}' ===> 'ab+'
print(re.findall('ab{0,}','abbb')) #'ab{0,}' ===> 'ab*'

#[]
print(re.findall('a[1*-]b','a1b a*b a-b')) #[]内的都为普通字符了,且如果-没有被转意的话,应该放到[]的开头或结尾
print(re.findall('a[^1*-]b','a1b a*b a-b a=b')) #[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[0-9]b','a1b a*b a-b a=b')) #[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[a-z]b','a1b a*b a-b a=b aeb')) #[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[a-zA-Z]b','a1b a*b a-b a=b aeb aEb')) #[]内的^代表的意思是取反,所以结果为['a=b']

#\# print(re.findall('a\\c','a\c')) #对于正则来说a\\c确实可以匹配到a\c,但是在python解释器读取a\\c时,会发生转义,然后交给re去执行,所以抛出异常
print(re.findall(r'a\\c','a\c')) #r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义
print(re.findall('a\\\\c','a\c')) #同上面的意思一样,和上面的结果一样都是['a\\c']

#():分组
print(re.findall('ab+','ababab123')) #['ab', 'ab', 'ab']
print(re.findall('(ab)+123','ababab123')) #['ab'],匹配到末尾的ab123中的ab
print(re.findall('(?:ab)+123','ababab123')) #findall的结果不是匹配的全部内容,而是组内的内容,?:可以让结果为匹配的全部内容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']
print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com">点击</a>'))#['href="http://www.baidu.com"']

#|
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))

re模块函数介绍

match(pattern, string, flags=0)

这个方法只从string 的头部匹配,匹配失败返回None,匹配成功返回Match object,注意是一个对象,可以通过返回的对象调用其内部方法获得匹配的信息。match对象的方法:

1.string:方法返回被匹配的字符串
2.start:返回指定的组在字符串中开始匹配的位置,组取值默认值为0
3.end:返回指定的组在字符串中开始匹配的位置,组取值默认值为0 
4.pos,endpos:返回起始和终止匹配位置 
5.group:返回指定组的匹配字符串
6.groupdict:返回有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。 
7.lastgroup:返回最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。 
8.lastindex:返回最后一个分组在文本中的索引 

9.span:返回(start(group), end(group)),即该组的起始和终止位置 

10.expand:可以实现分组之间顺序的调整

fullmatch(pattern, string, flags=0)

fullmatch()函数或方法就相当于给match()函数或方法的pattern或string参数加上行首边界元字符'^'和行尾边界元字符'$';即对整个字符串进行匹配

search(pattern, string, flags=0)

这个方法对整个字符串进行搜索,并返回第一个匹配的Match object对象,然后立即返回,若匹配失败返回None。返回的Match objec使用方法同上

findall(pattern, string, flags=0)

此方法以列表的形式返回能匹配的所有子串

sub(pattern, repl, string, count=0, flags=0)
subn(pattern, repl, string, count=0, flags=0)

sub函数返回的是被替换后的字符串,如果字符串中没有与正则表达式相匹配的内容,则返回原始字符串;subn函数除了返回被替换后的字符串,还会返回一个替换次数,它们是以元组的形式返回。

附加一个不错的关于re模块使用的博客:https://www.cnblogs.com/yyds/p/6953348.html

下面的代码是使用re模块完成一个字符串计算器:

# expression='1-2*((60+2*(-3-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'
'''
思路:
1、找出表达式中最深的(),若表达式内无(),则进入步骤 5
2、找出最深的()范围内乘法或者除法进入步骤 3,若表达式内没有乘法或者除法,则进入步骤 4
3、计算乘法或者除法,然后将结果拼接回表达式,回到步骤 2
4、计算表达式内剩余的加法或者减法,然后回到步骤 1
5、计算没有()的表达式,先计算乘法或者除法,然后计算加法或者减法,最后返回计算结果
'''

import sys
import re

def get_expression():
    while True:
        iput_expression = input("请输入你要计算的表达式[按下q:退出]:").strip()
        if iput_expression == 'q':  # 退出计算
            break
        elif len(iput_expression) == 0:
            continue
        else:
            iput_expression = re.sub('\s*', '', iput_expression)  # 去除空格
            return iput_expression

def caculate_mul_div(expression):
    result = re.search(r'\d+\.?\d*[\*\/]+[\+\-]?\d+\.?\d*', expression)
    if not result:
        return expression

    result = re.search(r'\d+\.?\d*[\*\/]+[\+\-]?\d+\.?\d*', expression).group()
    if len(result.split('*')) > 1:
        left, right = result.split('*')
        value = float(left) * float(right)
    else:
        left, right = result.split('/')
        if float(right) == 0:
            sys.exit("表达式中有除数为 0 存在,无法完成计算")
        else:
            value = float(left) / float(right)
            #value = int(value)
    print("计算前表达式为:%s"%expression)

    print("计算:%s = %s"%(result, value))

    left, right = re.split(r'\d+\.?\d*[\*\/]+[\+\-]?\d+\.?\d*', expression, 1)
    caculate_expression = "%s%s%s"%(left, value, right)
    print("计算后表达式为:%s"%caculate_expression)
    return caculate_mul_div(caculate_expression)

def calulate_add_sub(expression):
    expression = expression.replace('+-', '-')
    expression = expression.replace('--', '+')
    expression = expression.replace('-+', '-')
    expression = expression.replace('++', '+')

    print("+-符号去重后的表达式为:%s"%expression)
    result= re.search(r'\d+\.?\d*[\+\-]{1}\d+\.?\d*', expression)
    if not result:
        return expression

    result = re.search(r'[\-]?\d+\.?\d*[\+\-]{1}\d+\.?\d*', expression).group()
    if len(result.split('+')) > 1:
        left, right = result.split('+')
        value = float(left) + float(right)
    elif result.startswith('-'):
        left, mid, right = result.split('-')
        value = -float(mid) - float(right)
    else:
        left, right = result.split('-')
        value = float(left) - float(right)

    print("计算前表达式为:%s" % expression)

    print("计算:%s = %s" % (result, value))

    left, right = re.split(r'[\-]?\d+\.?\d*[\+\-]{1}\d+\.?\d*', expression, 1)
    caculate_expression = "%s%s%s" % (left, value, right)
    print("计算后表达式为:%s" % caculate_expression)
    return calulate_add_sub(caculate_expression)

def caculate(expression):
    if not re.search(r'\(([^()]+)\)',expression):    #判断表达式中是否还有小括号,无,则进行最后的计算并返回计算结果
        result = caculate_mul_div(expression)
        result = calulate_add_sub(result)
        return result
    max_deep_expression = re.search('\(([^()]+)\)',expression).group()  #找出层次最深的()
    max_deep_expression = max_deep_expression.strip('[\(\)]')
    result = caculate_mul_div(max_deep_expression)
    result = calulate_add_sub(result)

    left, max_deep_str, right = re.split(r'\(([^()]+)\)', expression, 1)
    expression = '%s%s%s'%(left, result, right)
    return caculate(expression)

if __name__ == '__main__':
    try:
        expression = get_expression()

        print("expression is:", expression)
        eval_reslut = eval(expression)
        caculate_result = caculate(expression)

        print("eval计算结果为:", eval_reslut)
        print("caculate计算结果为:", caculate_result)

        if float(eval_reslut) != float(caculate_result):
            print("eval计算结果与caculate计算结果不一致")

    except(SyntaxError, ValueError,TypeError):
        print("输入表达式有误,请重新输入!")

以上计算器的实现大家可以验证一下,有不对之处请指正。

猜你喜欢

转载自blog.csdn.net/zhaoyun_zzz/article/details/80531902