Python正则运算符优先级re.findall('(.)*',"abc")、re.findall('(.*)',"abc")、re.findall('(.?)*',"abc")的执行结果的影响分析

我们分别执行三个语句:

>>> re.findall('(.)*',"abc")	       
['c', '']
>>> re.findall('(.*)',"abc")
['abc', '']
>>> re.findall('(.?)*',"abc")	       
['', '']
>>>

可以看到三个执行结果完全不同,为什么会这样呢?老猿才开始没弄明白。我们使用《妙用re.sub分析正则表达式解析匹配过程》介绍的方法parsematch来分析一下这三个匹配语句:

>>> parsematch('(.)*',"abc")	       
第1次匹配,匹配情况:
    匹配子串group(0): abc,位置为:(0, 3)
    匹配子串group(1): c,位置为:(2, 3)
第2次匹配,m.lastindex=None,匹配情况:
    匹配子串group(0): ,位置为:(3, 3)

可以看到“(.)*”是每次匹配一个字母,再重复匹配过程直到匹配不到字母前停止,所以最后匹配结果是字符“c”,在“c”之后是空字符串,由于*的原因也会匹配成功。

>>> parsematch('(.*)',"abc")	       
第1次匹配,匹配情况:
    匹配子串group(0): abc,位置为:(0, 3)
    匹配子串group(1): abc,位置为:(0, 3)
第2次匹配,匹配情况:
    匹配子串group(0): ,位置为:(3, 3)
    匹配子串group(1): ,位置为:(3, 3)

可以看到“(.*)”是第一次匹配是匹配所有字符"abc",匹配到搜索文本结束,第二次匹配空字符串也是匹配成功。

>>> parsematch('(.?)*',"abc")	       
第1次匹配,匹配情况:
    匹配子串group(0): abc,位置为:(0, 3)
    匹配子串group(1): ,位置为:(3, 3)
第2次匹配,匹配情况:
    匹配子串group(0): ,位置为:(3, 3)
    匹配子串group(1): ,位置为:(3, 3)
>>>

可以看到“(.?)*”是每次匹配0-1个字符,再执行星号指定的重复匹配过程直到匹配到“c”之后,由于“abc”[3:3]也能匹配成功,因此最后匹配结果是空串。
第2次匹配“c”之后的空串开始,也是匹配成功,因此又会匹配到空串。

这是parsematch解析的匹配过程,但为什么会这样呢?这是由于匹配过程的优先级决定的,由于括号优先级高于“*”,因此匹配时先优先执行括号内的正则表达式匹配,括号内正则表达式匹配完之后再执行括号外的正则表达式的匹配。所以这三个才会有不同的处理结果。
其实关于正则表达式优先级的事情老猿才开始没有意识到,正是由于这三个语句的执行结果不同才引发了老猿的关注,最终补充了正则表达式优先级的知识章节。

本节相关知识:

  1. 关于正则表达式优先级请参考《第11.26 Python正则表达式运算符优先级》;
  2. 正则表达式解析匹配过程请参考《妙用re.sub分析正则表达式解析匹配过程》;
  3. 贪婪模式和非贪婪模式的匹配处理过程请参考《Python正则表达式W+和W*匹配过程的深入分析》的相关分析。

老猿Python,跟老猿学Python!
博客地址:https://blog.csdn.net/LaoYuanPython

请大家多多支持,点赞、评论和加关注!谢谢!

<think>嗯,用户问的是如何用完全括号法将中序表达式转化为后序表达式,并用Python实现。首先,我得回忆一下中序和后序表达式的区别。中序就是我们平常的写法,比如a + b,后序则是ab+。而完全括号法应该是指给每个操作加上括号,明确运算顺序,比如((a + b) * c)这样的。那转换的关键应该是处理运算符优先级和括号。 首先,我需要明确转换的步骤。完全括号法可能是指在中序表达式中显式添加所有括号,然后通过移动运算符的位置来得到后序表达式。比如,中缀表达式A + B * C会变成(A + (B * C)),然后后序就是ABC*+。那问题是怎么用算法实现这个过程。 用户提到用Python,所以可能需要用栈来处理运算符优先级。中序转后序的经典算法就是使用栈,但这里可能需要考虑括号的处理。完全括号法可能意味着每个子表达式都有括号,这样在处理时可能更容易,因为括号内的运算符优先级会被明确。 然后,我需要考虑如何处理括号。比如,遇到左括号就入栈,遇到右括号就弹出栈中的运算符直到左括号弹出。同时,运算符优先级需要比较,当前运算符优先级如果高于栈顶的就入栈,否则弹出栈顶的直到遇到更低优先级的或者左括号。 接下来,我得想具体的实现步骤。首先,可能需要将输入的中序表达式字符串拆分成token,比如数字、运算符、括号。这一步可能需要处理多位数和空格。比如,处理像“(A + B) * C”这样的表达式,拆分成['(', 'A', '+', 'B', ')', '*', 'C']。 然后,初始化两个栈,一个操作符栈,一个输出列表。遍历每个token,如果是操作数就直接加入输出;如果是左括号入栈;如果是右括号,弹出操作符到输出直到遇到左括号;如果是运算符,比较优先级,弹出栈顶优先级更高或相等的运算符到输出,然后当前运算符入栈。 但是这里用户提到的“完全括号法”可能意味着输入的表达式已经带有所有括号,比如每个操作都有括号,这样在处理时不需要考虑优先级,因为括号已经明确了运算顺序。比如,输入是((a + b) * (c - d)),这样转换时只需要按照括号的顺序处理运算符的位置。 如果是这样的话,算法可能更简单,因为每个运算符的位置由括号决定。当遇到一个右括号时,就将对应的运算符移到后面。例如,((a+b)*(c-d)),处理到每个右括号时,弹出运算符,得到后序表达式ab+cd-*。 但通常中序表达式可能没有完全括号,这时候需要先添加括号再转换。但用户的问题可能直接处理已经带有完全括号的中序表达式。这时候,转换步骤可能更直接,只需要处理括号内的运算符。 不过,用户的问题描述是“用完全括号法将中序表达式转化为后序表达式”,可能指的是通过完全括号的方式辅助转换,而输入的表达式可能并没有完全括号。所以可能需要先为原表达式添加完全括号,然后再转换为后序。或者,可能直接处理带有完全括号的中序表达式。 这个时候可能需要明确用户的输入形式。比如,假设输入的表达式已经是完全括号的,那么处理起来比较容易。否则,可能需要先解析表达式,根据优先级添加括号,然后再转换。 例如,输入的中序表达式是A + B * C,先转换为(A + (B * C)),然后再转换为后序ABC*+。但如何自动添加括号可能需要运算符优先级的判断。 不过,用户可能希望直接从中序表达式(可能没有完全括号)转换为后序,使用完全括号法作为中间步骤。或者,可能用户给出的输入已经是完全括号的,因此转换过程可以省略优先级判断,只需处理括号即可。 这时,我需要确定算法是否需要处理优先级。如果输入已经是完全括号的,那么转换时只需在遇到右括号时输出运算符。例如,对于((a + b) * (c - d)),遍历token,遇到右括号时,将对应的运算符弹出,得到ab+ cd- *。 因此,Python的实现可能需要一个栈来保存运算符,当遇到右括号时,将栈顶的运算符弹出并添加到输出列表中。而操作数则直接添加到输出中。 具体步骤可能如下: 1. 将输入的中缀表达式转换为tokens列表,处理多位数和运算符、括号。 2. 初始化一个运算符栈和一个输出列表。 3. 遍历每个token: a. 如果是操作数,添加到输出列表。 b. 如果是左括号,压入栈。 c. 如果是右括号,弹出栈中的运算符到输出,直到遇到左括号,弹出左括号但不输出。 d. 如果是运算符,比较与栈顶的优先级,弹出优先级更高或相等的运算符,直到遇到左括号或栈空,然后将当前运算符压入栈。 4. 最后将栈中剩余的运算符弹出到输出。 但这里的处理需要考虑运算符优先级,但如果表达式已经是完全括号的,那么每个运算符优先级已经被括号明确,此时可能不需要比较优先级,直接按括号处理即可。例如,每个运算符都在对应的括号内,所以遇到右括号时处理该运算符。 例如,假设输入表达式是((a + b) * (c - d)),那么处理到第一个右括号时,弹出+号,然后到第二个右括号时弹出-号,最后遇到*号,弹出。这样输出顺序会是ab+ cd- *。 但在这种情况下,运算符的处理顺序完全由括号决定,无需比较优先级。这时候算法可以简化为:遇到右括号时,弹出栈顶的运算符(因为每个运算符都在自己的括号内)。 不过这可能只在表达式完全括号的情况下成立。如果输入的表达式没有完全括号,那么需要处理优先级。因此,用户的问题可能需要处理两种情况:输入是否有完全括号。 但用户的问题明确提到“用完全括号法”转换,可能指的是无论输入是否有括号,都通过添加括号的方式处理,从而在转换时无需考虑优先级,只需处理括号即可。 这个时候,可能先要将原表达式转换为完全括号形式,然后再转换为后序。但如何自动添加括号可能比较复杂,需要考虑运算符优先级。例如,将A + B * C转换为(A + (B * C)),然后再转换为后序ABC*+。 但用户可能希望直接从中序转换为后序,而完全括号法是其中的一个步骤,即通过添加括号来辅助转换。或者,可能用户的问题是指使用栈的方法,类似于完全括号的处理方式,即显式处理括号和运算符优先级。 此时,可能需要结合标准的Shunting-yard算法,该算法可以处理中序转后序,同时处理括号和优先级。这个算法由Edsger Dijkstra提出,使用一个操作符栈和一个输出队列。 具体步骤: 遍历中缀表达式的每个元素: 1. 如果是数字,直接输出到结果队列。 2. 如果是左括号,压入栈。 3. 如果是右括号,弹出栈中的元素到输出队列,直到遇到左括号,左括号弹出但不输出。 4. 如果是运算符,比较其与栈顶运算符优先级: a. 如果栈顶是左括号,或者当前运算符优先级高于栈顶,压入栈。 b. 否则,弹出栈顶到输出队列,然后再次比较,直到满足条件,压入当前运算符。 遍历完成后,将栈中剩余运算符依次弹出到输出队列。 这样得到的就是后序表达式。而完全括号法可能在这个过程中隐式地被处理,因为运算符优先级由算法自动处理,相当于为每个子表达式添加了括号。 所以,用户的问题可能希望用Python实现Shunting-yard算法,将中缀表达式转换为后缀表达式,其中可能涉及到运算符优先级和括号的处理。 接下来,我需要编写Python代码,实现这个过程。首先需要处理表达式字符串,拆分成tokens。例如,处理类似“3 + 4 * 2 / (1 - 5)”这样的表达式,拆分成['3', '+', '4', '*', '2', '/', '(', '1', '-', '5', ')']。 拆分token可能需要处理多位数字和运算符,可以用正则表达式。例如,使用re.findall匹配数字、运算符和括号。 然后,定义运算符优先级,比如{'*':3, '/':3, '+':2, '-':2},左括号优先级最低。 然后,按照Shunting-yard算法处理每个token。 例如,对于表达式“( ( A + B ) * ( C - D ) )”,处理后的后序表达式是AB+CD-*。 现在,需要编写Python代码实现这个过程。可能的代码结构如下: 1. 定义运算符及其优先级。 2. 函数将中缀表达式字符串转换为tokens列表。 3. 使用栈处理tokens,生成后缀表达式。 另外,需要注意处理空格,以及多字符的操作数,比如变量名或者多位数。 例如,函数可以这样拆分token: import re def tokenize(expression): tokens = re.findall(r'\d+|\+|\-|\*|\/|\(|\)|\w+', expression) return tokens 然后,处理每个token: def infix_to_postfix(infix_expr): precedence = {'+':2, '-':2, '*':3, '/':3, '(':1} op_stack = [] postfix = [] tokens = tokenize(infix_expr) for token in tokens: if token.isalnum(): # 操作数 postfix.append(token) elif token == '(': op_stack.append(token) elif token == ')': top = op_stack.pop() while top != '(': postfix.append(top) top = op_stack.pop() else: # 运算符 while op_stack and precedence[op_stack[-1]] >= precedence[token]: postfix.append(op_stack.pop()) op_stack.append(token) # 处理栈中剩余运算符 while op_stack: postfix.append(op_stack.pop()) return ' '.join(postfix) 测试这个函数,例如输入“(A + B) * C”,转换为后序应该是“A B + C *”。 或者,输入“3 + 4 * 2 / ( 1 - 5 )”应该转换为“3 4 2 * 1 5 - / +”。 但可能需要处理更多情况,例如正确处理减号作为二元运算符,以及处理多位数字。 例如,输入“((A + B) * (C - D))”应该得到“A B + C D - *”。 这样,代码的大体结构就完成了。用户可能需要这样的Python实现。同时,需要解释每个步骤,例如栈的使用,运算符优先级的比较,括号的处理等。 总结来说,用户的问题需要将中序表达式转换为后序,使用完全括号法可能是指通过括号显式处理运算顺序,而Python实现可以通过Shunting-yard算法实现,处理括号和运算符优先级,从而得到正确的后序表达式。</think>要将中缀表达式(中序)转换为后缀表达式(后序),可以通过 **完全括号法** 辅助理解,并借助 **栈(Stack)** 实现运算符优先级和括号的自动处理。以下是分步实现和Python代码示例: --- ### **核心步骤** 1. **拆分表达式** 将输入字符串拆分为操作数、运算符和括号的列表,例如将 `"(A+B)*(C-D)"` 拆分为 `['(', 'A', '+', 'B', ')', '*', '(', 'C', '-', 'D', ')']`。 2. **定义运算符优先级** 设置运算符优先级(如 `*` 和 `/` 高于 `+` 和 `-`)。 3. **栈处理逻辑** - **操作数**:直接输出。 - **左括号 `(`**:压入栈。 - **右括号 `)`**:弹出栈顶元素到输出,直到遇到左括号。 - **运算符**:比较栈顶元素优先级,弹出优先级更高或相等的运算符,再将当前运算符压入栈。 4. **收尾处理** 遍历结束后,将栈中剩余运算符全部弹出。 --- ### **Python代码实现** ```python import re def tokenize(expression): # 将表达式拆分为操作数、运算符和括号 tokens = re.findall(r'\d+|\w+|[+\-*/()]', expression) return tokens def infix_to_postfix(infix_expr): precedence = {'+': 2, '-': 2, '*': 3, '/': 3, '(': 1} op_stack = [] postfix = [] tokens = tokenize(infix_expr) for token in tokens: if token.isalnum(): # 操作数(数字或变量) postfix.append(token) elif token == '(': op_stack.append(token) elif token == ')': # 弹出直到左括号 top = op_stack.pop() while top != '(': postfix.append(top) top = op_stack.pop() else: # 比较运算符优先级 while op_stack and precedence[op_stack[-1]] >= precedence[token]: postfix.append(op_stack.pop()) op_stack.append(token) # 弹出栈内剩余运算符 while op_stack: postfix.append(op_stack.pop()) return ' '.join(postfix) # 示例 infix = "((A + B) * (C - D))" postfix = infix_to_postfix(infix) print(postfix) # 输出:A B + C D - * ``` --- ### **示例解析** 输入中缀表达式:`((A + B) * (C - D))` 转换过程: 1. 拆分后的 tokens:`['(', '(', 'A', '+', 'B', ')', '*', '(', 'C', '-', 'D', ')', ')']` 2. 栈操作和输出: - 遇到 `A`、`B`,直接输出。 - 遇到 `+`,压入栈。 - 遇到 `)`,弹出 `+` 输出,得到 `A B +`。 - 遇到 `*`,压入栈。 - 遇到 `C`、`D`,直接输出。 - 遇到 `-`,压入栈。 - 遇到 `)`,弹出 `-` 输出,得到 `C D -`。 - 弹出栈中的 `*`,最终输出 `A B + C D - *`。 --- ### **关键点** - **完全括号法** 通过显式括号明确运算顺序,算法隐式处理优先级。 - **栈** 用于暂存运算符,确保按优先级和括号顺序输出。 - 代码通过正则表达式 `re.findall` 拆分表达式,支持多位数和变量名。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LaoYuanPython

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值