Как извлечь простые числовые выражения чисел из строки? - PullRequest
2 голосов
/ 14 апреля 2019

Я хочу закодировать конвертер единиц измерения, и мне нужно извлечь указанное значение из единицы во входной строке.

Чтобы обеспечить удобство использования при использовании конвертера, я хочу, чтобы пользователь мог вводить значение и единицу измерения в одной строке. Моя проблема в том, что я хочу извлечь цифры и буквы, чтобы я мог сообщить программе единицу и значение и сохранить их в двух разных переменных. Для извлечения букв я использовал оператор 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 ]

1 голос
/ 14 апреля 2019

Традиционно в Python, как и во многих других языках, экспоненты начинаются с буквы e или E.Хотя 5 * 10**6 не является допустимым литералом с плавающей запятой, 5e6 определенно таковым является.

Об этом следует помнить в будущем, но это не решит вашу проблему с оператором in,Проблема в том, что in может проверить только то, что вы уже знаете.Что, если вместо этого вы вводите 5e-8 km?

Вы должны начать с однозначно четкого определения того, как вы определяете границу между числом и единицами в строке.Например, единицы могут быть последним непрерывным битом нецифровых символов в вашей строке.

Затем можно разбить строку, используя регулярные выражения .Поскольку первая часть может быть произвольным выражением, вы можете оценить его с помощью чего-нибудь простого: 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)
0 голосов
/ 14 апреля 2019

Существует много способов решения этой простой проблемы, используя 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}")

Я также рекомендую вам прочитать этот пост Почему использование 'eval' - плохая практика?

0 голосов
/ 14 апреля 2019

Кажется, что вы ищете функцию eval, как отмечено в ответе @ Rasgel. Документация здесь

Как отмечали некоторые люди, это представляет большую угрозу безопасности.

Чтобы обойти это, я могу придумать 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 :)
0 голосов
/ 14 апреля 2019

Если у вас есть строка типа 5*106 и вы хотите преобразовать это число в число с плавающей точкой, вы можете использовать функцию eval().

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