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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从字符串中提取简单的数字表达式数字?相关的知识,希望对你有一定的参考价值。

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

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

看来你正在寻找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.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?

另一答案

如果您有像5*106and这样的字符串想要将此数字转换为浮点数,则可以使用eval()函数。

>>> float(eval('5*106'))
530.0
另一答案

如果您有像5*106and这样的字符串想要将此数字转换为浮点数,则可以使用eval()函数。

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

以上是关于如何从字符串中提取简单的数字表达式数字?的主要内容,如果未能解决你的问题,请参考以下文章

Java如何从字符串中提取数字

Java如何从字符串中提取数字

如何从字符串中提取数字并获取整数数组?

在C#中提取字符串末尾的数字

从字符串中提取分数、小数和数字的正则表达式[关闭]

JavaScript 正则表达式 - 从单词旁边提取数字