我正在研究一个“复制粘贴计算器”,它可以检测复制到系统剪贴板的任何数学表达式,对它们进行评估并将答案复制到准备粘贴的剪贴板上。但是,虽然代码使用了eval()函数,但考虑到用户通常知道他们正在复制什么,我并不十分担心。话虽如此,我想找到一种更好的方法,而不会给计算带来障碍(例如,删除计算乘法或指数的能力)。
以下是我的代码的重要部分:
#! python3
import pyperclip, time
parsedict = {"×": "*",
"÷": "/",
"^": "**"} # Get rid of anything that cannot be evaluated
def stringparse(string): # Remove whitespace and replace unevaluateable objects
a = string
a = a.replace(" ", "")
for i in a:
if i in parsedict.keys():
a = a.replace(i, parsedict[i])
print(a)
return a
def calculate(string):
parsed = stringparse(string)
ans = eval(parsed) # EVIL!!!
print(ans)
pyperclip.copy(str(ans))
def validcheck(string): # Check if the copied item is a math expression
proof = 0
for i in mathproof:
if i in string:
proof += 1
elif "http" in string: #TODO: Create a better way of passing non-math copies
proof = 0
break
if proof != 0:
calculate(string)
def init(): # Ensure previous copies have no effect
current = pyperclip.paste()
new = current
main(current, new)
def main(current, new):
while True:
new = pyperclip.paste()
if new != current:
validcheck(new)
current = new
pass
else:
time.sleep(1.0)
pass
if __name__ == "__main__":
init()
问:我应该使用什么代替eval()来计算答案?
答案 0 :(得分:5)
您应该使用ast.parse
:
import ast
try:
tree = ast.parse(expression, mode='eval')
except SyntaxError:
return # not a Python expression
if not all(isinstance(node, (ast.Expression,
ast.UnaryOp, ast.unaryop,
ast.BinOp, ast.operator,
ast.Num)) for node in ast.walk(tree)):
return # not a mathematical expression (numbers and operators)
result = eval(compile(tree, filename='', mode='eval'))
请注意,为简单起见,这允许所有一元运算符(+
,-
,~
,not
)以及算术和按位二元运算符({{ 1}},+
,-
,*
,/
,%
//
,**
,<<
,>>
,&
,|
),但不是逻辑或比较运算符。如果应该直接改进或扩展允许的运算符。
答案 1 :(得分:0)
在不使用eval
的情况下,您必须实现解析器,或使用现有的软件包,例如simpleeval
(我不是作者,还有其他人,但是我已经成功测试了其中一个)
一行,加上导入:
>>> from simpleeval import simpleeval
>>> simpleeval.simple_eval("(45 + -45) + 34")
34
>>> simpleeval.simple_eval("(45 - 22*2) + 34**2")
1157
现在,如果我尝试通过导入模块来破解计算器:
>>> simpleeval.simple_eval("import os")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "K:\CODE\COTS\python\simpleeval\simpleeval.py", line 466, in simple_eval
return s.eval(expr)
File "K:\CODE\COTS\python\simpleeval\simpleeval.py", line 274, in eval
return self._eval(ast.parse(expr.strip()).body[0].value)
AttributeError: 'Import' object has no attribute 'value'
抓到!错误的错误消息来自simpleeval
可以评估可以有选择地通过字典传递的变量。捕获AttributeError
异常以拦截格式错误的表达式。不需要eval
。
答案 2 :(得分:-1)
input_string = '1+1-1*4+1'
result = 0
counter = -1
for ch in range(len(input_string)):
if counter == ch:
continue
if input_string[ch] in ['-', '+', '/', '*', '**']:
next_value = int(input_string[ch+1])
if input_string[ch] == '-':
result -= next_value
counter = ch+1
elif input_string[ch] == '+':
result += next_value
counter = ch+1
elif input_string[ch] == '*':
result *= next_value
counter = ch+1
elif input_string[ch] == '/':
result /= next_value
counter = ch+1
elif input_string[ch] == '**':
result **= next_value
counter = ch+1
else:
result = int(input_string[ch])
print(result)
Output : The original string is : '1+1-1*4+1' The evaluated result is : 5