如何从字符串中提取简单的数字表达式数字?

时间:2019-04-14 12:31:10

标签: python string numbers extract

我想对单位转换器进行编码,我需要从输入字符串中的单位中提取给定值。

为了在使用转换器时提供用户友好的体验,我希望用户能够在同一字符串中输入值和单位。我的问题是我想提取数字和字母,以便可以告诉程序单位和值并将它们存储在两个不同的变量中。为了提取字母,我使用了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'.

那么我该如何使用类似的方法将更复杂的数字提取到浮点数中?

4 个答案:

答案 0 :(得分:1)

传统上,在Python中,就像在许多其他语言中一样,指数以字母eE为前缀。虽然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)

答案 1 :(得分:0)

似乎您正在寻找eval函数,如@Rasgel的答案所述。 Documentation here

正如某些人指出的那样,这带来了很大的安全风险。

为避免这种情况,我可以想到2种方法:

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 :)

答案 2 :(得分:0)

使用str.splitregular expressionsevalast.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?

答案 3 :(得分:-3)

如果您有一个类似5*106的字符串,并且想要将此数字转换为浮点数,则可以使用eval()函数。

>>> float(eval('5*106'))
530.0