Si j'ai cette chaîne:
2 + 24 * 48/32
quelle est l'approche la plus efficace pour créer cette liste:
['2', '+', '24', '*', '48', '/', '32']
Il se trouve que les jetons que vous souhaitez fractionner sont déjà Python, vous pouvez donc utiliser le module tokenize
intégré. Il s'agit presque d'une ligne:
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']
Vous pouvez utiliser split
à partir du module re
.
re.split (motif, chaîne, maxsplit = 0, drapeaux = 0)
Fractionner la chaîne par les occurrences du motif. Si des parenthèses de capture sont utilisées dans le modèle, le texte de tous les groupes du modèle est également renvoyé dans la liste résultante.
Exemple de code:
import re
data = re.split(r'(\D)', '2+24*48/32')
\RÉ
Lorsque l'indicateur UNICODE n'est pas spécifié,\D correspond à tout caractère non numérique; cela équivaut à l'ensemble [^ 0-9].
>>> import re
>>> re.findall(r'\d+|\D+', '2+24*48/32=10')
['2', '+', '24', '*', '48', '/', '32', '=', '10']
Correspond à des chiffres consécutifs ou à des non-chiffres consécutifs.
Chaque correspondance est renvoyée en tant que nouvel élément dans la liste.
Selon l'utilisation, vous devrez peut-être modifier l'expression régulière. Comme si vous devez faire correspondre des nombres avec un point décimal.
>>> re.findall(r'[0-9\.]+|[^0-9\.]+', '2+24*48/32=10.1')
['2', '+', '24', '*', '48', '/', '32', '=', '10.1']
Cela ressemble à un problème d'analyse, et donc je suis obligé de présenter une solution basée sur des techniques d'analyse.
Bien qu'il puisse sembler que vous souhaitiez "diviser" cette chaîne, je pense que ce que vous voulez réellement faire, c'est la "symboliser". La tokenisation ou lexxing est l'étape de compilation avant l'analyse. J'ai modifié mon exemple d'origine dans une édition pour implémenter ici un analyseur décent récursif approprié. C'est le moyen le plus simple d'implémenter un analyseur à la main.
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 __== '__main__':
main()
La mise en œuvre de la manipulation des parenthèses est laissée comme un exercice pour le lecteur. Cet exemple fera correctement la multiplication avant l'addition.
C'est un problème d'analyse, donc ni regex ni split () ne sont la "bonne" solution. Utilisez plutôt un générateur d'analyseur.
Je regarderais de près pyparsing . Il y a également eu quelques articles décents sur le pyparsing dans le Python Magazine .
s = "2 + 24 * 48/32"
p = re.compile (r '(\ W +)')
p.split (s)
Expressions régulières:
>>> import re
>>> splitter = re.compile(r'([+*/])')
>>> splitter.split("2+24*48/32")
Vous pouvez développer l'expression régulière pour inclure tout autre caractère que vous souhaitez fractionner.
Une autre solution serait d'éviter d'écrire une calculatrice comme celle-là. L'écriture d'un analyseur RPN est beaucoup plus simple et n'a pas l'ambiguïté inhérente à l'écriture mathématique avec la notation infixe.
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']
Ça fera l'affaire. J'ai déjà rencontré ce genre de problème auparavant.
Cela ne répond pas exactement à la question, mais je crois que cela résout ce que vous essayez de réaliser. Je voudrais l'ajouter en tant que commentaire, mais je n'ai pas encore la permission de le faire.
Personnellement, je profiterais de la fonctionnalité mathématique de Python directement avec exec:
expression = "2 + 24 * 48/32"
exec "result =" + expression
impression résultat
38