36 Stimmen

Wie kann ich eine Zeichenkette in eine Liste aufteilen?

Wenn ich diese Zeichenfolge habe:

2+24*48/32

wie diese Liste am effizientesten zu erstellen ist:

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

0 Stimmen

Sie wollen eine Zeichenkette in eine Liste aufteilen, aber Sie wollen nicht .split() verwenden, weil es eine Liste zurückgibt? Du widersprichst dir selbst. Wenn Sie keine Liste wollen, was wollen Sie dann tun wollen?

1 Stimmen

@Jim: Ich denke, Jibmo meint, dass split() nur die Angabe eines Trennzeichens erlaubt, so dass er es einmal für '+', einmal für '-', einmal für '/' usw. aufrufen müsste.

2 Stimmen

Sorry für die schlechte Erklärung, was ich meinte, ist, dass Split eine Liste zurückgibt, was bedeutet, für die zweite Split, ich muss jetzt über Zeichenfolgen innerhalb einer Liste zu iterieren. syntaktisch falsches Beispiel. string = "2+2-2" Liste = string.split(+) gibt ['2', '+', '2-2'] jetzt muss ich über 3 Zeichenfolgen iterieren

51voto

Glyph Punkte 30137

Zufälligerweise sind die Token, die Sie aufteilen wollen, bereits Python-Token, so dass Sie die eingebaute tokenize Modul. Es ist fast ein Einzeiler: dieses Programm:

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

erzeugt diese Ausgabe:

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

0 Stimmen

Tolle Antwort, ich wusste gar nicht, dass es dieses Modul gibt :)

0 Stimmen

Anstatt manuell zuzuweisen STRING=1 können Sie die Konstante aus der token Modul, indem es eine from token import STRING . Dies ist besonders nützlich, wenn Sie mehrere Token-Konstanten benötigen.

1 Stimmen

Warum sollte eine so komplizierte Antwort so hoch bewertet werden? Es ist eine ziemlich einfache Frage. Was ist aus der Suche nach der saubersten, prägnantesten Antwort geworden?

36voto

readonly Punkte 323452

Sie können verwenden split von der re Modul.

re.split(muster, zeichenkette, maxsplit=0, flags=0)

Zeichenkette durch die Vorkommen des Musters teilen. Wenn einfangende Klammern im Muster verwendet werden, wird der Text aller Gruppen im Muster auch als Teil der resultierenden Liste zurückgegeben.

Beispiel-Code:

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

\D

Wenn das UNICODE-Flag nicht angegeben ist, \D passt auf jede nicht-zifferige ; dies entspricht der Menge [^0-9].

18voto

Jerub Punkte 40038

Dies sieht nach einem Parsing-Problem aus, und so sehe ich mich gezwungen, eine Lösung auf der Grundlage von Parsing-Techniken zu präsentieren.

Auch wenn es den Anschein hat, dass Sie diese Zeichenfolge "aufteilen" wollen, denke ich, dass Sie sie eigentlich "tokenisieren" wollen. Tokenisierung oder Lexxing ist der Kompilierungsschritt vor dem Parsen. Ich habe mein ursprüngliches Beispiel in einem Edit geändert, um hier einen richtigen rekursiven anständigen Parser zu implementieren. Dies ist der einfachste Weg, einen Parser von Hand zu implementieren.

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

Die Implementierung der Behandlung von Klammern wird dem Leser als Übung überlassen. In diesem Beispiel wird die Multiplikation korrekt vor der Addition ausgeführt.

0 Stimmen

Ich lese jetzt etwas über Tokenisierung, um es zu verstehen. Ich kann also nicht sagen, wo das Problem liegt, aber ich denke, es liegt daran, dass dieses Skript * und / gleichzeitig auswertet, was nicht korrekt ist. 8/2*2 Diese Zeichenkette sollte ein Ergebnis von 2 ausgeben, aber sie gibt ein Ergebnis von 8 aus.

0 Stimmen

Entschuldigen Sie mich, ich habe bomdas immer wörtlich genommen. Es stellt sich heraus, dass Multiplikation und Division in der Reihenfolge der Vorhersage gleich sind und dass das, was zuerst auftritt, zuerst ausgewertet wird

0 Stimmen

Unter tokenize : Warum verwenden re um Leerzeichen über eine integrierte String-Funktion zu entfernen?

18voto

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

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

Entspricht aufeinanderfolgenden Ziffern oder aufeinanderfolgenden Nicht-Ziffern.

Jede Übereinstimmung wird als neues Element in der Liste zurückgegeben.

Je nach Verwendung müssen Sie den regulären Ausdruck möglicherweise ändern. Zum Beispiel, wenn Sie Zahlen mit einem Dezimalpunkt abgleichen müssen.

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

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

6voto

Ber Punkte 37347

Dies ist ein Parsing-Problem, daher sind weder regex noch split() die "gute" Lösung. Verwenden Sie stattdessen einen Parser-Generator.

Ich würde mir Folgendes genau ansehen pyparsing . Es gab auch einige anständige Artikel über pyparsing in der Python-Magazin .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X