Подтвердить что ты не робот

Как разбить строку на список?

Если у меня есть эта строка:

2 + 24 * 48/32

Каков наиболее эффективный подход для создания этого списка:

['2', '+', '24', '*', '48', '/', '32']

4b9b3361

Ответ 1

Так получилось, что токены, которые вы хотите разделить, уже являются токенами Python, поэтому вы можете использовать встроенный модуль tokenize. Это почти однострочный:

from cStringIO import StringIO
from tokenize import generate_tokens
STRING = 1
list(token[STRING] for token 
     in generate_tokens(StringIO('2+24*48/32').readline)
     if token[STRING])
['2', '+', '24', '*', '48', '/', '32']

Ответ 2

Вы можете использовать split из модуля re.

re.split(pattern, string, maxsplit = 0, flags = 0)

Разделить строку на наличие шаблонов. Если скобки будут скопированы используются в шаблоне, тогда текст всех групп в шаблоне также возвращается как часть результирующего списка.

Пример кода:

import re
data = re.split(r'(\D)', '2+24*48/32')

\ D

Если флаг UNICODE не указан, \D соответствует любой нецифровой персонаж; это эквивалентно множеству [^ 0-9].

Ответ 3

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

Хотя может показаться, что вы хотите "разбить" эту строку, я думаю, что вы действительно хотите сделать это "tokenize". Токсификация или lexxing - это этап компиляции перед разбором. Я исправил свой оригинальный пример в редакции, чтобы реализовать правильный рекурсивный достойный парсер. Это самый простой способ реализовать парсер вручную.

import re

patterns = [
    ('number', re.compile('\d+')),
    ('*', re.compile(r'\*')),
    ('/', re.compile(r'\/')),
    ('+', re.compile(r'\+')),
    ('-', re.compile(r'\-')),
]
whitespace = re.compile('\W+')

def tokenize(string):
    while string:

        # strip off whitespace
        m = whitespace.match(string)
        if m:
            string = string[m.end():]

        for tokentype, pattern in patterns:
            m = pattern.match(string)
            if m:
                yield tokentype, m.group(0)
                string = string[m.end():]

def parseNumber(tokens):
    tokentype, literal = tokens.pop(0)
    assert tokentype == 'number'
    return int(literal)

def parseMultiplication(tokens):
    product = parseNumber(tokens)
    while tokens and tokens[0][0] in ('*', '/'):
        tokentype, literal = tokens.pop(0)
        if tokentype == '*':
            product *= parseNumber(tokens)
        elif tokentype == '/':
            product /= parseNumber(tokens)
        else:
            raise ValueError("Parse Error, unexpected %s %s" % (tokentype, literal))

    return product

def parseAddition(tokens):
    total = parseMultiplication(tokens)
    while tokens and tokens[0][0] in ('+', '-'):
        tokentype, literal = tokens.pop(0)
        if tokentype == '+':
            total += parseMultiplication(tokens)
        elif tokentype == '-':
            total -= parseMultiplication(tokens)
        else:
            raise ValueError("Parse Error, unexpected %s %s" % (tokentype, literal))

    return total

def parse(tokens):
    tokenlist = list(tokens)
    returnvalue = parseAddition(tokenlist)
    if tokenlist:
        print 'Unconsumed data', tokenlist
    return returnvalue

def main():
    string = '2+24*48/32'
    for tokentype, literal in tokenize(string):
        print tokentype, literal

    print parse(tokenize(string))

if __name__ == '__main__':
    main()

Реализация обработки скобок оставлена ​​в качестве упражнения для читателя. Этот пример будет корректно выполнять умножение перед добавлением.

Ответ 4

>>> import re
>>> re.findall(r'\d+|\D+', '2+24*48/32=10')

['2', '+', '24', '*', '48', '/', '32', '=', '10']

Соответствует последовательным цифрам или последовательным нецифровым номерам.

Каждое совпадение возвращается как новый элемент в списке.

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

>>> re.findall(r'[0-9\.]+|[^0-9\.]+', '2+24*48/32=10.1')

['2', '+', '24', '*', '48', '/', '32', '=', '10.1']

Ответ 5

Это проблема синтаксического анализа, поэтому ни одно из регулярных выражений, а не split(), является "хорошим" решением. Вместо этого используйте генератор синтаксического анализатора.

Я бы внимательно посмотрел на pyparsing. Там также были некоторые достойные статьи о пирафинге в Python Magazine.

Ответ 6

Регулярные выражения:

>>> import re
>>> splitter = re.compile(r'([+*/])')
>>> splitter.split("2+24*48/32")

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

Ответ 7

  

s = "2 + 24 * 48/32"

         

p = re.compile(r '(\ W +)')

         

p.split(ы)

  

Ответ 8

Другим решением для этого было бы вообще не писать такой калькулятор. Написание парсера RPN намного проще и не имеет какой-либо двусмысленности, присущей написанию математики с нотной инфиксной записью.

import operator, math
calc_operands = {
    '+': (2, operator.add),
    '-': (2, operator.sub),
    '*': (2, operator.mul),
    '/': (2, operator.truediv),
    '//': (2, operator.div),
    '%': (2, operator.mod),
    '^': (2, operator.pow),
    '**': (2, math.pow),
    'abs': (1, operator.abs),
    'ceil': (1, math.ceil),
    'floor': (1, math.floor),
    'round': (2, round),
    'trunc': (1, int),
    'log': (2, math.log),
    'ln': (1, math.log),
    'pi': (0, lambda: math.pi),
    'e': (0, lambda: math.e),
}

def calculate(inp):
    stack = []
    for tok in inp.split():
        if tok in self.calc_operands:
            n_pops, func = self.calc_operands[tok]
            args = [stack.pop() for x in xrange(n_pops)]
            args.reverse()
            stack.append(func(*args))
        elif '.' in tok:
            stack.append(float(tok))
        else:
            stack.append(int(tok))
    if not stack:
        raise ValueError('no items on the stack.')
    return stack.pop()
    if stack:
        raise ValueError('%d item(s) left on the stack.' % len(stack))

calculate('24 38 * 32 / 2 +')

Ответ 9

>>> import re
>>> my_string = "2+24*48/32"
>>> my_list = re.findall(r"-?\d+|\S", my_string)
>>> print my_list

['2', '+', '24', '*', '48', '/', '32']

Это сделает трюк. Раньше я сталкивался с подобными проблемами.

Ответ 10

Я уверен, что Тим имел в виду

splitter = re.compile(r'([\D])'). 

Если вы скопируете то, что у него есть, вы получите digits не operators.

Ответ 11

Почему бы просто не использовать SymPy? Он должен делать то, что вы пытаетесь достичь.

Ответ 12

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

Я лично воспользовался бы функциями математики Python непосредственно с помощью exec:

выражение = "2 + 24 * 48/32"
exec "result =" + выражение
распечатать результат
38