我想编码单位转换器,我需要从输入字符串中的单位中提取给定值。
为了在使用转换器时提供用户友好的体验,我希望用户能够在同一个字符串中输入值和单位。我的问题是我想提取数字和字母,以便我可以告诉程序单位和值,并将它们存储在两个不同的变量中。为了提取字母,我使用了in
运算符,并且正常工作。我还找到了一个从输入中获取数字的解决方案,但这对于带有指数的值不起作用。
a = str(input("Type in your wavelength: "))
if "mm" in a:
print("Unit = Millimeter")
b = float(a.split()[0])
在567 mm
中存储简单的输入,如b
作为浮点数,但我希望能够提取像5*10**6 mm
这样的输入,但它说
could not convert string to float: '5*10**6'.
那么我可以使用什么来将更复杂的数字提取到浮点数中呢?
传统上,在Python中,与许多其他语言一样,指数的前缀是字母e
或E
。虽然5 * 10**6
不是一个有效的浮点文字,但5e6
绝对是。
这是未来要记住的事情,但它不会解决您使用in
运算符的问题。问题是in
只能检查你已经知道的东西是否存在。如果你输入的是5e-8 km
怎么办?
您应首先明确定义如何识别字符串中数字和单位之间的边界。例如,单位可能是字符串中最后一个连续的非数字字符。
然后你可以使用regular expressions拆分字符串。由于第一部分可以是任意表达式,因此您可以使用ast.literal_eval
这样简单的方法来评估它。表达式越复杂,解析器也就越复杂。
这是一个让你入门的例子:
from ast import literal_eval
import re
pattern = re.compile(r'(.*[\d\.])\s*(\D+)')
data = '5 * 10**6 mm'
match = pattern.fullmatch(data)
if not match:
raise ValueError('Invalid Expression')
num, units = match.groups()
num = literal_eval(num)
看来你正在寻找eval
功能,如@ Rasgel的回答中所述。 Documentation here
正如一些人所指出的那样,它会带来很大的安全隐患。
为了避免这种情况,我可以想到两种方式:
1.将eval
与正则表达式结合起来
如果你只想做基本的算术运算,如加法,减法和2**4
或类似的那样,那么你可以使用正则表达式来首先删除任何非数字,非算术运算字符。
import re
a = str(input("Type in your wavelength: "))
if "mm" in a:
print("Unit = Millimeter")
# After parsing the units,
# Remove anything other than digits, +, -, *, /, . (floats), ! (factorial?) and ()
# If you require any other symbols, add them in
pruned_a = re.sub(r'[^0-9\*\+\-\/\!\.\(\)]', "", a)
result = eval(pruned_a)
2.确保eval实际上没有评估python代码中的任何本地或全局变量。
result = eval(expression, {'__builtins__': None}, {})
(上面的代码来自另一个Stackoverflow答案:Math Expression Evaluation - 可能还有其他你可能感兴趣的解决方案)
综合
import re
a = str(input("Type in your wavelength: "))
if "mm" in a:
print("Unit = Millimeter")
# After parsing the units,
# Remove anything other than digits, +, -, *, /, . (floats), ! (factorial?) and ()
# If you require any other symbols, add them in
pruned_a = re.sub(r'[^0-9\*\+\-\/\!\.\(\)]', "", a)
result = eval(pruned_a, {'__builtins__': None}, {}) #to be extra safe :)
有很多方法可以解决这个简单的问题,使用str.split
,regular expressions
,eval
,ast.literal_eval
...在这里,我建议你有自己的安全例程来评估简单的数学表达式,代码如下:
import re
import ast
import operator
def safe_eval(s):
bin_ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.itruediv,
ast.Mod: operator.mod,
ast.Pow: operator.pow
}
node = ast.parse(s, mode='eval')
def _eval(node):
if isinstance(node, ast.Expression):
return _eval(node.body)
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return bin_ops[type(node.op)](_eval(node.left), _eval(node.right))
else:
raise Exception('Unsupported type {}'.format(node))
return _eval(node.body)
if __name__ == '__main__':
text = str(input("Type in your wavelength: "))
tokens = [v.strip() for v in text.split()]
if len(tokens) < 2:
raise Exception("expected input: <wavelength expression> <unit>")
wavelength = safe_eval("".join(tokens[:-1]))
dtype = tokens[-1]
print(f"You've typed {wavelength} in {dtype}")
我也建议你阅读这篇文章Why is using 'eval' a bad practice?
如果您有像5*106
and这样的字符串想要将此数字转换为浮点数,则可以使用eval()
函数。
>>> float(eval('5*106'))
530.0
如果您有像5*106
and这样的字符串想要将此数字转换为浮点数,则可以使用eval()
函数。
>>> float(eval('5*106'))
530.0