如何将字符串拆分为列表?

时间:2020-03-06 14:19:23  来源:igfitidea点击:

如果我有这个字符串:

2+24*48/32

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

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

解决方案

常用表达:

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

我们可以展开正则表达式以包括要分割的其他任何字符。

s = "2+24*48/32"
      
      p = re.compile(r'(\W+)')
      
      p.split(s)

我们可以从re模块使用split

re.split(模式,字符串,maxsplit = 0,标志= 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.

示例代码:

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].

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

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

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

括号的实现留给读者练习。本示例将在加法之前正确进行乘法。

我确定蒂姆的意思是

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

如果准确复制他的失败记录,则只会得到"数字",而不是"运算符"。

>>> 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']

对此的另一种解决方案是完全避免编写这样的计算器。编写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 +')

为什么不只使用SymPy?它应该做我们想要达到的目标。

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

我会仔细分析pyparsing。在Python Magazine中也有一些关于pyparsing的不错的文章。

碰巧我们要拆分的令牌已经是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']

这不能完全回答问题,但我相信它可以解决我们要实现的目标。我将其添加为评论,但我尚未获得批准。

我个人将直接通过exec充分利用Python的数学功能:

expression = "2+24*48/32"

  exec "result = " + expression

  print result

  38