36

如果我有这个字符串:

2+24*48/32

创建此列表的最有效方法是什么:

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

4

11 回答 11

51

碰巧您要拆分的令牌已经是 Python 令牌,因此您可以使用内置tokenize模块。它几乎是一条线;这个程序:

from io import StringIO
from tokenize import generate_tokens

STRING = 1
print(
    list(
        token[STRING]
    for token in generate_tokens(StringIO("2+24*48/32").readline)
    if token[STRING]
    )
)

产生这个输出:

['2', '+', '24', '*', '48', '/', '32']
于 2008-09-21T16:25:42.300 回答
36

You can use split from the re module.

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

Split string by the occurrences of pattern. If capturing parentheses are used in pattern, then the text of all groups in the pattern are also returned as part of the resulting list.

Example code:

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

\D

When the UNICODE flag is not specified, \D matches any non-digit character; this is equivalent to the set [^0-9].

于 2008-09-17T23:25:56.403 回答
18

这看起来像是一个解析问题,因此我不得不提出一个基于解析技术的解决方案。

虽然您似乎想要“拆分”这个字符串,但我认为您真正想要做的是“标记化”它。标记化或词法分析是解析之前的编译步骤。我在编辑中修改了我的原始示例,以在此处实现适当的递归体面解析器。这是手动实现解析器的最简单方法。

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

括号处理的实现留给读者作为练习。这个例子将在加法之前正确地进行乘法运算。

于 2008-09-17T23:54:14.090 回答
18
>>> 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']
于 2008-09-18T02:39:38.903 回答
6

这是一个解析问题,因此 regex 和 split() 都不是“好”的解决方案。请改用解析器生成器。

我会仔细研究pyparsingPython Magazine中也有一些关于 pyparsing 的不错的文章。

于 2008-09-19T07:37:18.587 回答
5

s = "2+24*48/32"

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

p.split(s)

于 2008-09-17T23:25:52.833 回答
4

Regular expressions:

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

You can expand the regular expression to include any other characters you want to split on.

于 2008-09-17T23:21:58.800 回答
4

另一个解决方案是完全避免编写这样的计算器。编写 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 +')
于 2008-09-18T03:07:27.947 回答
1
>>> import re
>>> my_string = "2+24*48/32"
>>> my_list = re.findall(r"-?\d+|\S", my_string)
>>> print my_list

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

这会成功的。我以前遇到过这种问题。

于 2012-01-14T16:21:15.617 回答
0

我确定蒂姆的意思是

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

如果您完全复制他所记录的内容,您只会得到digits不是operators.

于 2008-09-18T00:45:18.117 回答
0

这并不能完全回答问题,但我相信它可以解决您想要实现的目标。我会将其添加为评论,但我还没有这样做的权限。

我个人会直接使用 exec 来利用 Python 的数学功能:

表达式 = "2+24*48/32"
exec "结果 = " + 表达式
打印结果
38

于 2010-08-19T00:38:43.097 回答