841 Stimmen

Strings in Wörter mit mehreren Wortbegrenzern aufteilen

Ich denke, was ich tun möchte, ist eine ziemlich häufige Aufgabe, aber ich habe keine Referenz im Web gefunden. Ich habe Text mit Satzzeichen und möchte eine Liste der Wörter.

"Hey, you - what are you doing here!?"

sollte sein

['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Aber Pythons str.split() funktioniert nur mit einem Argument, also habe ich alle Wörter mit dem Satzzeichen, nachdem ich mit Leerzeichen aufgeteilt habe. Irgendwelche Ideen?

7 Stimmen

13 Stimmen

Python's str.split() funktioniert auch ohne Argumente überhaupt

511voto

Louis LC Punkte 5046

Ein weiterer schneller Weg, dies ohne RegExp zu tun, besteht darin, die Zeichen zuerst zu ersetzen, wie unten angegeben:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']

93 Stimmen

Schnell und einfach, aber perfekt für meinen Fall (meine Trennzeichen waren eine kleine, bekannte Menge)

10 Stimmen

Perfekt für den Fall, dass Sie keinen Zugriff auf die RE-Bibliothek haben, wie zum Beispiel bestimmte kleine Mikrocontroller. :-)

19 Stimmen

Ich denke, dass dies auch expliziter ist als RE, also ist es irgendwie Anfänger freundlicher. Manchmal braucht man nicht die allgemeine Lösung für alles.

407voto

Eric O Lebigot Punkte 85676

So viele Antworten, aber ich kann keine Lösung finden, die effizient das macht, was der Titel der Frage wörtlich verlangt (Aufteilen nach mehreren möglichen Trennzeichen—viele Antworten teilen stattdessen nach allem auf, was kein Wort ist, was anders ist). Hier also eine Antwort auf die Frage im Titel, die auf dem standardmäßigen und effizienten re Modul von Python basiert:

>>> import re  # Wird aufteilen nach: ,  - ! ? :
>>> filter(None, re.split(r"[, \-!?:]+", "Hey, you-what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

wo:

  • das [...] passt zu einem der aufgelisteten Trennzeichen,
  • das \- im regulären Ausdruck ist hier, um die spezielle Interpretation von - als Zeichenbereichsindikator (wie in A-Z) zu verhindern,
  • das + überspringt ein oder mehrere Trennzeichen (es könnte dank des filter() weggelassen werden, aber das würde unnötigerweise leere Zeichenfolgen zwischen passenden Einzelzeichen-Trennzeichen erzeugen),
  • die Verwendung eines rohen Strings r"…" macht es explizit, dass \ im String so bleiben soll, wie es ist (und keinen speziellen Charakter einführt)—dies ist nützlich für Python 3.12+—, und
  • filter(None, …) entfernt die leeren Zeichenfolgen, die möglicherweise durch führende und abschließende Trennzeichen erzeugt werden (da leere Zeichenfolgen einen falschen booleschen Wert haben).

Diese re.split() teilt genau mit "mehreren Trennzeichen", wie in der Frage im Titel gefordert.

Diese Lösung ist außerdem immun gegen Probleme mit Nicht-ASCII-Zeichen in Wörtern, die in einigen anderen Lösungen gefunden wurden (siehe den ersten Kommentar zu ghostdog74's Antwort).

Das re Modul ist viel effizienter (in Geschwindigkeit und Kürze) als Schleifen und Tests in Python "von Hand" zu machen!

3 Stimmen

"Ich kann keine Lösung finden, die effizient das tut, was der Titel der Frage buchstäblich verlangt" - die zweite Antwort tut das, vor 5 Jahren veröffentlicht: stackoverflow.com/a/1059601/2642204.

26 Stimmen

Diese Antwort teilt nicht an den Trennzeichen auf (aus einer Reihe von mehreren Trennzeichen): Sie teilt stattdessen an allem, was nicht alphanumerisch ist. Nichtsdestotrotz stimme ich zu, dass die Absicht des ursprünglichen Beitragenden wahrscheinlich ist, nur die Wörter zu behalten, anstatt einige Satzzeichen zu entfernen.

0 Stimmen

EOL: Ich denke, diese Antwort spaltet sich auf einer Reihe von mehreren Trennzeichen. Wenn Sie Nicht-Alphanumerika zum String hinzufügen, die nicht spezifiziert sind, wie zum Beispiel Unterstrich, werden sie, wie erwartet, nicht getrennt.

61voto

ghostdog74 Punkte 305138

Ein anderer Weg, ohne regex

import string
punc = string.punctuation
thestring = "Hey, you - what are you doing here!?"
s = list(thestring)
''.join([o for o in s if not o in punc]).split()

8 Stimmen

Diese Lösung ist tatsächlich besser als die akzeptierte. Es funktioniert ohne ASCII-Zeichen, versuchen Sie "Hey, du - was machst du hier María!?". Die akzeptierte Lösung wird nicht mit dem vorherigen Beispiel funktionieren.

4 Stimmen

Ich denke, hier liegt ein kleines Problem vor ... Dein Code wird Zeichen anhängen, die durch Satzzeichen getrennt sind, und sie daher nicht aufteilen ... Wenn ich mich nicht irre, sollte deine letzte Zeile sein: ''.join([o if not o in string.punctuation else ' ' for o in s]).split()

0 Stimmen

Die reguläre Ausdrucksbibliothek kann notfalls so angepasst werden, dass sie Unicode-Konventionen für Zeichen akzeptiert. Darüber hinaus hat dies dasselbe Problem, das die akzeptierte Lösung früher hatte: wie es jetzt ist, wird an Apostrophen getrennt. Sie möchten o for o in s if (o in not string.punctuation or o == "'"), aber dann wird es zu kompliziert für eine Einzeiler, wenn wir auch cedbeus Patch einbeziehen.

42voto

Dave Punkte 3050

Pro-Tipp: Verwenden Sie string.translate für die schnellsten Zeichenkettenoperationen, die Python zu bieten hat.

Ein paar Beweise...

Zuerst der langsame Weg (Entschuldigung pprzemek):

>>> import timeit
>>> S = 'Hey, du - was machst du hier!?'
>>> def my_split(s, seps):
...     res = [s]
...     for sep in seps:
...         s, res = res, []
...         for seq in s:
...             res += seq.split(sep)
...     return res
... 
>>> timeit.Timer('my_split(S, punctuation)', 'from __main__ import S,my_split; from string import punctuation').timeit()
54.65477919578552

Dann verwenden wir re.findall() (wie im vorgeschlagenen Antwort gegeben). VIEL schneller:

>>> timeit.Timer('findall(r"\w+", S)', 'from __main__ import S; from re import findall').timeit()
4.194725036621094

Zuletzt verwenden wir translate:

>>> from string import translate,maketrans,punctuation 
>>> T = maketrans(punctuation, ' '*len(punctuation))
>>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit()
1.2835021018981934

Erklärung:

string.translate ist in C implementiert und anders als viele Zeichenkettenmanipulationsfunktionen in Python erzeugt string.translate keine neue Zeichenkette. Es ist also so schnell wie möglich für Zeichenaustausch.

Es ist jedoch etwas umständlich, da es eine Übersetzungstabelle benötigt, um diese Magie zu vollbringen. Sie können eine Übersetzungstabelle mit der Bequemlichkeitsfunktion maketrans() erstellen. Das Ziel hier ist es, alle unerwünschten Zeichen in Leerzeichen zu übersetzen. Ein-Eins-Ersatz. Auch hier wird keine neue Daten erzeugt. Also ist das schnell!

Dann verwenden wir das gute alte split(). split() wird standardmäßig auf alle Leerzeichenzeichen angewendet, die sie gruppiert für die Aufteilung zusammen. Das Ergebnis wird die Liste der Wörter sein, die Sie möchten. Und dieser Ansatz ist fast 4-mal schneller als re.findall()!

5 Stimmen

Ich habe hier einen Test gemacht, und wenn Sie Unicode verwenden müssen, ist die Verwendung von patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S) schneller als translate, weil Sie den String vor dem Anwenden der Transform-Funktion kodieren und jedes Element in der Liste nach dem Teilen dekodieren müssen, um zu Unicode zurückzukehren.

0 Stimmen

Sie können die Translate-Implementierung in einer einzigen Zeile haben und sicherstellen, dass S nicht unter den Splittern ist mit: s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])

0 Stimmen

Kein Problem. Du vergleichst Äpfel mit Birnen. ;) Meine Lösung in Python 3 funktioniert immer noch ;P und hat Unterstützung für Multi-Char-Trennzeichen. :) Versuche das auf einfache Weise, ohne einen neuen String zuzuweisen. :) Aber ja, meine Lösung ist auf das Analysieren von Befehlszeilenparametern beschränkt und nicht zum Beispiel auf ein Buch.

30voto

pprzemek Punkte 2365

Ich stand vor einem ähnlichen Dilemma und wollte das 're'-Modul nicht verwenden.

def my_split(s, seps):
    res = [s]
    for sep in seps:
        s, res = res, []
        for seq in s:
            res += seq.split(sep)
    return res

print my_split('1111  2222 3333;4444,5555;6666', [' ', ';', ','])
['1111', '', '2222', '3333', '4444', '5555', '6666']

1 Stimmen

Ich mag das. Nur eine Anmerkung, die Reihenfolge der Trennzeichen spielt eine Rolle. Entschuldigung, wenn das offensichtlich ist.

2 Stimmen

Warum nicht das re-Modul verwenden, das sowohl schneller als auch klarer ist (nicht dass reguläre Ausdrücke besonders klar sind, aber weil es kürzer und direkter ist)?

0 Stimmen

Es gibt viele Versionen von Python, nicht nur die auf python.org. Nicht alle von ihnen haben das re-Modul, besonders wenn Sie einbetten, schneiden Sie einfach alles ab, was Sie können.

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