¿Cómo divido una cadena en una lista?

Si tengo esta cadena:

2 + 24 * 48/32

¿Cuál es el enfoque más eficiente para crear esta lista:

[‘2’, ‘+’, ’24’, ‘*’, ’48’, ‘/’, ’32’]

Da la casualidad de que los tokens que desea dividir ya son tokens de Python, por lo que puede usar el módulo tokenize incorporado. Es casi una sola línea:

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

Puedes usar split desde el módulo re .

re.split (patrón, cadena, maxsplit = 0, flags = 0)

Dividir cadena por las ocurrencias de patrón. Si se usan paréntesis de captura en el patrón, entonces el texto de todos los grupos en el patrón también se devuelve como parte de la lista resultante.

Código de ejemplo:

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

\RE

Cuando el indicador UNICODE no está especificado, \ D coincide con cualquier carácter que no sea un dígito; esto es equivalente al conjunto [^ 0-9].

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

Hace coincidir dígitos consecutivos o no dígitos consecutivos.

Cada coincidencia se devuelve como un nuevo elemento en la lista.

Dependiendo del uso, es posible que deba modificar la expresión regular. Por ejemplo, si necesitas hacer coincidir los números con un punto decimal.

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

Esto parece un problema de análisis, por lo que estoy obligado a presentar una solución basada en técnicas de análisis.

Si bien puede parecer que quieres “dividir” esta cadena, creo que lo que realmente quieres hacer es “marcarla”. La tokenización o lexxing es el paso de comstackción antes de analizar. He enmendado mi ejemplo original en una edición para implementar un analizador decente recursivo adecuado aquí. Esta es la forma más fácil de implementar un analizador a mano.

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

La implementación del manejo de los soportes se deja como un ejercicio para el lector. Este ejemplo hará correctamente la multiplicación antes de la sum.

Este es un problema de análisis, por lo que ni regex ni split () son la solución “buena”. Utilice un generador de analizador en su lugar.

Yo miraría de cerca a pyparsing . También ha habido algunos artículos decentes sobre pyparsing en la revista Python .

s = “2 + 24 * 48/32”

p = re.compile (r ‘(\ W +)’)

p.split (s)

Expresiones regulares:

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

Puede expandir la expresión regular para incluir cualquier otro carácter que desee dividir.

Otra solución para esto sería evitar escribir una calculadora así. Escribir un analizador RPN es mucho más simple, y no tiene nada de la ambigüedad inherente a escribir matemáticas con notación de infijo.

 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 +') 
 >>> import re >>> my_string = "2+24*48/32" >>> my_list = re.findall(r"-?\d+|\S", my_string) >>> print my_list ['2', '+', '24', '*', '48', '/', '32'] 

Esto hará el truco. He encontrado este tipo de problema antes.

¿Por qué no usar simplemente SymPy ? Debería hacer lo que estás tratando de lograr.

Esto no responde exactamente a la pregunta, pero creo que resuelve lo que estás tratando de lograr. Lo agregaría como un comentario, pero aún no tengo permiso para hacerlo.

Personalmente, aprovecharía la funcionalidad matemática de Python directamente con exec:

expresión = “2 + 24 * 48/32”
exec “result =” + expresión
resultado de impresión
38

estoy seguro de que Tim quiso decir

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

Si copia exactamente lo que tiene, solo obtendrá los digits no los operators .