Как мне разбить строку на список? - PullRequest
36 голосов
/ 18 сентября 2008

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

2 + 24 * 48/32

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

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

Ответы [ 11 ]

50 голосов
/ 21 сентября 2008

Так получилось, что токены, которые вы хотите разделить, уже являются токенами 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']
36 голосов
/ 18 сентября 2008

Вы можете использовать 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].

18 голосов
/ 18 сентября 2008
>>> 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']
18 голосов
/ 18 сентября 2008

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

Хотя может показаться, что вы хотите «разбить» эту строку, я думаю, что вы на самом деле хотите «токенизировать» ее. Tokenization или 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()

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

6 голосов
/ 19 сентября 2008

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

Я бы присмотрелся к pyparsing . В журнале Python .

было несколько приличных статей о pyparsing.
5 голосов
/ 18 сентября 2008

с = "2 + 24 * 48/32"

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

p.split (s)

4 голосов
/ 18 сентября 2008

Другим решением этой проблемы было бы вообще не писать такой калькулятор. Написание синтаксического анализатора 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 +')
4 голосов
/ 18 сентября 2008

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

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

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

1 голос
/ 14 января 2012
>>> import re
>>> my_string = "2+24*48/32"
>>> my_list = re.findall(r"-?\d+|\S", my_string)
>>> print my_list

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

Это поможет. Я сталкивался с такой проблемой раньше.

0 голосов
/ 19 августа 2010

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

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

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

...