Teilen Sie Zeichenfolgen in Wörter mit mehreren Wortbegrenzungsbegrenzern


670

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 Interpunktion 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, daher habe ich alle Wörter mit der Interpunktion, nachdem ich mich mit Leerzeichen getrennt habe. Irgendwelche Ideen?



6
Python str.split()funktioniert auch ohne Argumente
Ivan Vinogradov

Antworten:


467

Ein Fall, in dem reguläre Ausdrücke gerechtfertigt sind:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

2
Vielen Dank. Immer noch interessiert - wie kann ich den in diesem Modul verwendeten Algorithmus implementieren? Und warum erscheint es nicht im String-Modul?
Ooboo

29
Reguläre Ausdrücke können zunächst entmutigend sein, sind aber sehr mächtig. Der reguläre Ausdruck '\ w +' bedeutet "ein Wortzeichen (az usw.), das ein- oder mehrmals wiederholt wird". Hier gibt es ein HOWTO zu regulären Python-Ausdrücken: amk.ca/python/howto/regex
RichieHindle

324
Dies ist nicht die Antwort auf die Frage. Dies ist eine Antwort auf eine andere Frage, die für diese spezielle Situation funktioniert. Es ist, als ob jemand gefragt hätte "Wie mache ich eine Linkskurve?" Und die Antwort mit der höchsten Stimme lautete "Nimm die nächsten drei Rechtskurven". Es funktioniert für bestimmte Kreuzungen, gibt aber nicht die erforderliche Antwort. Ironischerweise ist die Antwort ist in re, einfach nicht findall. Die Antwort unten re.split()ist überlegen.
Jesse Dhillon

4
@JesseDhillon "Alle Teilzeichenfolgen nehmen, die aus einer Folge von Wortzeichen bestehen" und "Auf alle Teilzeichenfolgen aufteilen, die aus einer Folge von Nichtwortzeichen bestehen" sind buchstäblich nur verschiedene Arten, dieselbe Operation auszudrücken. Ich bin mir nicht sicher, warum Sie eine der Antworten als überlegen bezeichnen würden.
Mark Amery

4
@TMWP: Der Apostroph bedeutet, dass ein Wort wie don'tein einzelnes Wort behandelt wird, anstatt in donund aufgeteilt zu werden t.
RichieHindle

574

re.split ()

re.split (Muster, String [, maxsplit = 0])

Teilen Sie die Zeichenfolge durch das Auftreten von Mustern. Wenn im Muster Erfassungsklammern verwendet werden, wird der Text aller Gruppen im Muster ebenfalls als Teil der resultierenden Liste zurückgegeben. Wenn maxsplit ungleich Null ist, treten höchstens maxsplit-Teilungen auf, und der Rest der Zeichenfolge wird als letztes Element der Liste zurückgegeben. (Hinweis zur Inkompatibilität: In der ursprünglichen Version von Python 1.5 wurde maxsplit ignoriert. Dies wurde in späteren Versionen behoben.)

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']

13
Diese Lösung hat den Vorteil, dass sie leicht angepasst werden kann, um auch Unterstriche aufzuteilen, was die Findall-Lösung nicht tut: print re.split ("\ W + | _", "Testing this_thing") 'ergibt: [' Testing ',' this ' , 'Ding']
Emil Stenström

63
Nun , wenn ich nur könnte daran denken , den Unterschied zwischen \w, \W, \sund \S. Wer dachte, dass die Großschreibung einer Flagge ihre Bedeutung umkehren sollte, muss durch den Kopf geschossen werden.
ArtOfWarfare

1
Ein häufiger Anwendungsfall beim Teilen von Zeichenfolgen ist das Entfernen leerer Zeichenfolgeneinträge aus dem Endergebnis. Ist das mit dieser Methode möglich? re.split ('\ W +', 'abc') führt zu ['', 'a', 'b', 'c', '']
Scott Morken

3
@ArtOfWarfare Es ist üblich, den shiftSchlüssel zu verwenden, um das Gegenteil von etwas zu tun. ctrl+zrückgängig machen vs. ctrl+shift+zfür wiederholen. Also shift woder Wwäre das Gegenteil von w.
Frank Vel

1
Diese Antwort sollte oben stehen - es ist die einzige, die den Fragentitel genau beantwortet.
Kranach

380

Eine andere schnelle Möglichkeit, dies ohne regulären Ausdruck zu tun, besteht darin, zuerst die Zeichen wie folgt zu ersetzen:

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

71
Schnell und schmutzig, aber perfekt für meinen Fall (meine Separatoren waren ein kleines, bekanntes Set)
Andy Baker

7
Perfekt für den Fall, dass Sie keinen Zugriff auf die RE-Bibliothek haben, z. B. für bestimmte kleine Mikrocontroller. :-)
tu-Reinstate Monica-dor duh

11
Ich denke, das ist auch expliziter als RE, also ist es irgendwie noob-freundlich. Manchmal brauchen Sie keine allgemeine Lösung für alles
Adam Hughes

Genial. Ich hatte ein .split () in einer Situation mit mehreren Eingaben und musste abfangen, wenn der Benutzer, ich, die Eingaben durch ein Leerzeichen und nicht durch ein Komma trennte. Ich wollte gerade aufgeben und mit re neu besetzen, aber Ihre .replace () -Lösung traf den Nagel auf den Kopf. Vielen Dank.
JayJay123

Sie erhalten eine falsche Antwort, wenn Sie keine Leerzeichen und keine anderen Zeichen teilen möchten.
Ahmed Amr

307

So viele Antworten, aber ich kann keine Lösung finden, die effizient das tut, was der Titel der Fragen buchstäblich verlangt (Aufteilung auf mehrere mögliche Trennzeichen - stattdessen werden viele Antworten auf alles aufgeteilt, was kein Wort ist, das anders ist). Hier ist eine Antwort auf die Frage im Titel, die sich auf Pythons Standard- und effizientes reModul stützt :

>>> import re  # Will be splitting on: , <space> - ! ? :
>>> filter(None, re.split("[, \-!?:]+", "Hey, you - what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

wo:

  • das […]entspricht einem der darin aufgeführten Trennzeichen,
  • Das \-im regulären Ausdruck soll hier die spezielle Interpretation -als Zeichenbereichsindikator (wie in A-Z) verhindern,
  • Das +überspringt ein oder mehrere Trennzeichen (es könnte dank des weggelassen werden filter(), aber dies würde unnötigerweise leere Zeichenfolgen zwischen übereinstimmenden Trennzeichen erzeugen) und
  • filter(None, …) Entfernt die leeren Zeichenfolgen, die möglicherweise durch führende und nachfolgende Trennzeichen erstellt wurden (da leere Zeichenfolgen einen falschen booleschen Wert haben).

Dies re.split()genau "teilt sich mit mehreren Trennzeichen", wie im Fragentitel gefordert.

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

Das reModul ist viel effizienter (in Geschwindigkeit und Präzision) als Python-Schleifen und Tests "von Hand"!


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

17
Diese Antwort wird nicht an Trennzeichen (aus mehreren Trennzeichen) aufgeteilt, sondern an alles, was nicht alphanumerisch ist. Trotzdem stimme ich zu, dass die Absicht des Originalplakats wahrscheinlich darin besteht, nur die Wörter beizubehalten, anstatt einige Satzzeichen zu entfernen.
Eric O Lebigot

EOL: Ich denke, diese Antwort wird auf mehrere Trennzeichen aufgeteilt. Wenn Sie der Zeichenfolge nicht alphanumerische Zeichen hinzufügen, die nicht angegeben sind, wie z. B. Unterstriche, werden sie nicht wie erwartet aufgeteilt.
GravityWell

@GravityWell: Ich bin nicht sicher, ob ich das verstehe: Können Sie ein konkretes Beispiel geben?
Eric O Lebigot

3
@EOL: Ich habe gerade festgestellt, dass ich durch Ihren Kommentar "Diese Antwort teilt sich nicht ..." verwirrt war. Ich dachte, "dies" bezog sich auf Ihre Antwort, aber jetzt ist mir klar, dass Sie Gimels Antwort meinten. Ich denke, DIESE Antwort (die Antwort, auf die ich kommentiere) ist die beste Antwort :)
GravityWell

56

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
Diese Lösung ist tatsächlich besser als die akzeptierte. Versuchen Sie es ohne ASCII-Zeichen "Hey, you - what are you doing here María!?". Die akzeptierte Lösung funktioniert nicht mit dem vorherigen Beispiel.
Christopher Ramírez

4
Ich denke, hier gibt es ein kleines Problem ... Ihr Code fügt Zeichen hinzu, die durch Interpunktion getrennt sind, und teilt sie daher nicht auf ... Wenn ich mich nicht irre, sollte Ihre letzte Zeile lauten:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu

In der Bibliothek für reguläre Ausdrücke können bei Bedarf Unicode-Konventionen für Zeichen akzeptiert werden. Darüber hinaus hat dies das gleiche Problem wie die akzeptierte Lösung: Wie es jetzt ist, teilt es sich auf Apostrophe auf. Vielleicht möchten Sie o for o in s if (o in not string.punctuation or o == "'"), aber dann wird es für einen Einzeiler zu kompliziert, wenn wir auch den Patch von cedbeu hinzufügen.
Daniel H

Hier gibt es noch ein anderes Problem. Selbst wenn wir die Änderungen von @cedbeu berücksichtigen, funktioniert dieser Code nicht, wenn die Zeichenfolge so etwas wie ist "First Name,Last Name,Street Address,City,State,Zip Code"und wir nur durch ein Komma teilen möchten ,. Gewünschte Ausgabe wäre: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Was wir stattdessen bekommen:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker

4
Diese Lösung ist furchtbar ineffizient: Zuerst wird die Liste in einzelne Zeichen zerlegt , dann wird der gesamte Satz von Satzzeichen für jedes einzelne Zeichen in der ursprünglichen Zeichenfolge durchlaufen, dann werden die Zeichen wieder zusammengesetzt und dann erneut aufgeteilt. All diese "Bewegungen" sind im Vergleich zu einer auf regulären Ausdrücken basierenden Lösung ebenfalls sehr kompliziert: Auch wenn die Geschwindigkeit in einer bestimmten Anwendung keine Rolle spielt, ist keine komplizierte Lösung erforderlich. Da das reModul Standard ist und sowohl Lesbarkeit als auch Geschwindigkeit bietet, verstehe ich nicht, warum es vermieden werden sollte.
Eric O Lebigot

39

Pro-Tipp: Verwenden Sie string.translatefür die schnellsten String-Operationen, die Python hat.

Einige Beweise ...

Erstens der langsame Weg (sorry pprzemek):

>>> import timeit
>>> S = 'Hey, you - what are you doing here!?'
>>> 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

Als nächstes verwenden wir re.findall()(wie in der vorgeschlagenen Antwort angegeben). Viel schneller:

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

Schließlich 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

Erläuterung:

string.translateist in C implementiert und erzeugt im Gegensatz zu vielen String-Manipulationsfunktionen in Python string.translate keinen neuen String. Es ist also so schnell wie möglich, um Zeichenfolgen zu ersetzen.

Es ist jedoch etwas umständlich, da es eine Übersetzungstabelle benötigt, um diese Magie auszuführen. Mit der maketrans()Convenience-Funktion können Sie eine Übersetzungstabelle erstellen. Ziel ist es, alle unerwünschten Zeichen in Leerzeichen zu übersetzen. Ein Eins-zu-Eins-Ersatz. Auch hier werden keine neuen Daten erzeugt. Das geht also schnell !

Als nächstes verwenden wir gute alte split(). split()Standardmäßig werden alle Leerzeichen verarbeitet und für die Aufteilung gruppiert. Das Ergebnis ist die Liste der gewünschten Wörter. Und dieser Ansatz ist fast 4x schneller als re.findall()!


4
Ich habe hier einen Test durchgeführt, und wenn Sie Unicode verwenden müssen, ist die Verwendung patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)schneller als die Übersetzung, da Sie die Zeichenfolge vor dem Anwenden der Transformation codieren und jedes Element in der Liste nach dem Teilen dekodieren müssen, um zum Unicode zurückzukehren.
Rafael S. Calsaverini

Sie können die Übersetzungsimplementierung s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
Kochfeldern

Keine genommen. Sie vergleichen Äpfel und Orangen. ;) Meine Lösung in Python 3 funktioniert immer noch; P und unterstützt Trennzeichen mit mehreren Zeichen. :) Versuchen Sie dies auf einfache Weise, ohne einen neuen String zuzuweisen. :) aber wahr, meine beschränkt sich auf das Parsen von Befehlszeilenparametern und nicht auf ein Buch zum Beispiel.
pprzemek

Sie sagen "erzeugt keine neue Zeichenfolge", was bedeutet, dass es an einer bestimmten Zeichenfolge funktioniert? Ich habe es jetzt mit Python 2.7 getestet und es ändert keine oroginale Zeichenfolge und gibt eine neue zurück.
Prokop Hapala

26

Ich hatte ein ähnliches 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
Ich mag das. Nur eine Anmerkung, die Reihenfolge der Trennzeichen ist wichtig. Entschuldigung, wenn das offensichtlich ist.
CrizCraig

2
Warum nicht das reModul verwenden, das sowohl viel schneller als auch klarer ist (nicht, dass reguläre Ausdrücke besonders klar sind, sondern weil es viel kürzer und direkter ist)?
Eric O Lebigot

13

Erstens möchte ich anderen zustimmen, dass die Regex- oder str.translate(...)basierten Lösungen am leistungsfähigsten sind. Für meinen Anwendungsfall war die Leistung dieser Funktion nicht signifikant, daher wollte ich Ideen hinzufügen, die ich mit diesen Kriterien berücksichtigte.

Mein Hauptziel war es, Ideen aus einigen der anderen Antworten in eine Lösung zu verallgemeinern, die für Zeichenfolgen funktionieren kann, die mehr als nur Regex-Wörter enthalten (dh die explizite Teilmenge von Interpunktionszeichen gegenüber Whitelist-Wortzeichen auf die schwarze Liste setzen).

Beachten Sie, dass bei jedem Ansatz auch die Verwendung string.punctuationeiner manuell definierten Liste in Betracht gezogen werden kann .

Option 1 - re

Ich war überrascht zu sehen, dass bisher keine Antwort re.sub (...) verwendet . Ich finde es eine einfache und natürliche Herangehensweise an dieses Problem.

import re

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

words = re.split(r'\s+', re.sub(r'[,\-!?]', ' ', my_str).strip())

In dieser Lösung habe ich den Aufruf nach re.sub(...)innen verschachtelt re.split(...)- aber wenn die Leistung kritisch ist, kann das Kompilieren des regulären Ausdrucks von außen von Vorteil sein - für meinen Anwendungsfall war der Unterschied nicht signifikant, daher bevorzuge ich Einfachheit und Lesbarkeit.

Option 2 - str.replace

Dies sind noch ein paar Zeilen, aber es hat den Vorteil, dass es erweiterbar ist, ohne prüfen zu müssen, ob Sie einem bestimmten Zeichen in Regex entkommen müssen.

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

replacements = (',', '-', '!', '?')
for r in replacements:
    my_str = my_str.replace(r, ' ')

words = my_str.split()

Es wäre schön gewesen, die str.replace stattdessen der Zeichenfolge zuordnen zu können, aber ich glaube nicht, dass dies mit unveränderlichen Zeichenfolgen möglich ist, und während die Zuordnung zu einer Liste von Zeichen funktionieren würde, würde jeder Ersatz für jedes Zeichen ausgeführt klingt übertrieben. (Bearbeiten: Ein Funktionsbeispiel finden Sie in der nächsten Option.)

Option 3 - functools.reduce

(In Python 2 reduceist es im globalen Namespace verfügbar, ohne es aus functools zu importieren.)

import functools

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

replacements = (',', '-', '!', '?')
my_str = functools.reduce(lambda s, sep: s.replace(sep, ' '), replacements, my_str)
words = my_str.split()

Hm, eine andere Methode ist zu verwenden str.translate- sie ist nicht Unicode-fähig, aber höchstwahrscheinlich schneller als andere Methoden und kann daher in einigen Fällen gut sein: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))Auch hier ist es obligatorisch, Ersetzungen als Zeichenfolge zu haben, nicht als Tupel oder Liste.
MarSoft

@ MarSoft Danke! Ich erwähnte diese oben in der Antwort, entschied mich jedoch, sie nicht hinzuzufügen, da vorhandene Antworten sie bereits gut diskutierten.
Taylor Edmiston

10
join = lambda x: sum(x,[])  # a.k.a. flatten1([[1],[2,3],[4]]) -> [1,2,3,4]
# ...alternatively...
join = lambda lists: [x for l in lists for x in l]

Dann wird dies ein Drei-Liner:

fragments = [text]
for token in tokens:
    fragments = join(f.split(token) for f in fragments)

Erläuterung

Dies ist, was in Haskell als Listenmonade bekannt ist. Die Idee hinter der Monade ist, dass Sie einmal "in der Monade" "in der Monade bleiben", bis Sie etwas herausholt. Angenommen, Sie ordnen die Python- range(n) -> [1,2,...,n]Funktion in Haskell einer Liste zu. Wenn das Ergebnis eine Liste ist, wird es direkt an die Liste angehängt, sodass Sie so etwas wie erhaltenmap(range, [3,4,1]) -> [0,1,2,0,1,2,3,0] . Dies wird als Map-Append (oder Mappend oder so ähnlich) bezeichnet. Die Idee hier ist, dass Sie diese Operation haben, die Sie anwenden (auf ein Token aufteilen), und wann immer Sie dies tun, fügen Sie das Ergebnis in die Liste ein.

Sie können dies in eine Funktion abstrahieren und haben tokens=string.punctuationstandardmäßig.

Vorteile dieses Ansatzes:

  • Dieser Ansatz kann (im Gegensatz zu naiven Regex-basierten Ansätzen) mit Token beliebiger Länge arbeiten (was Regex auch mit fortgeschrittener Syntax tun kann).
  • Sie sind nicht auf bloße Token beschränkt. Sie könnten anstelle jedes Tokens eine beliebige Logik haben, zum Beispiel könnte einer der "Token" eine Funktion sein, die sich nach verschachtelten Klammern aufteilt.

Ordentliche Haskell-Lösung, aber IMO kann dies klarer ohne Zuordnung in Python geschrieben werden.
Vlad der Impala

@Goose: Der Punkt war, dass die 2-Zeilen-Funktion map_then_appendverwendet werden kann, um ein Problem zu einem 2-Zeilen- Problem zu machen, sowie viele andere Probleme, die viel einfacher zu schreiben sind. Die meisten anderen Lösungen verwenden das reModul für reguläre Ausdrücke , bei dem es sich nicht um Python handelt. Aber ich war unzufrieden damit, wie ich meine Antwort unelegant und aufgebläht erscheinen lasse, wenn sie wirklich prägnant ist ... Ich werde sie bearbeiten ...
Ninjagecko

soll das in Python wie geschrieben funktionieren? Mein fragmentsErgebnis ist nur eine Liste der Zeichen in der Zeichenfolge (einschließlich der Token).
Rick unterstützt Monica

@ RickTeachey: Es funktioniert für mich sowohl in Python2 als auch in Python3.
Ninjagecko

hmmmm. Vielleicht ist das Beispiel etwas mehrdeutig. Ich habe den Code in der Antwort alle Arten von verschiedenen Arten - versuche einschließlich aufweist fragments = ['the,string'], fragments = 'the,string'oder fragments = list('the,string')keiner von ihnen den richtigen Output produzieren.
Rick unterstützt Monica

5

Versuche dies:

import re

phrase = "Hey, you - what are you doing here!?"
matches = re.findall('\w+', phrase)
print matches

Dies wird gedruckt ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']


4

Verwenden Sie zweimal ersetzen:

a = '11223FROM33344INTO33222FROM3344'
a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,')

Ergebnisse in:

['11223', '33344', '33222', '3344']

4

Ich mag re , aber hier ist meine Lösung ohne:

from itertools import groupby
sep = ' ,-!?'
s = "Hey, you - what are you doing here!?"
print [''.join(g) for k, g in groupby(s, sep.__contains__) if not k]

sep .__ enthält__ ist eine Methode, die vom Operator 'in' verwendet wird. Im Grunde ist es das gleiche wie

lambda ch: ch in sep

ist aber hier bequemer.

groupby bekommt unseren String und unsere Funktion. Mit dieser Funktion wird die Zeichenfolge in Gruppen aufgeteilt: Wenn sich ein Funktionswert ändert, wird eine neue Gruppe generiert. Also, sep .__ enthält__ ist genau das, was wir brauchen.

groupby gibt eine Folge von Paaren zurück, wobei pair [0] ein Ergebnis unserer Funktion ist und pair [1] eine Gruppe ist. Mit 'if not k' filtern wir Gruppen mit Trennzeichen heraus (da ein Ergebnis von sep .__ enthält__ auf Trennzeichen True ist). Nun, das ist alles - jetzt haben wir eine Folge von Gruppen, in denen jedes ein Wort ist (Gruppe ist eigentlich iterierbar, also verwenden wir join , um es in einen String umzuwandeln).

Diese Lösung ist recht allgemein gehalten, da sie eine Funktion zum Trennen von Zeichenfolgen verwendet (Sie können nach jeder gewünschten Bedingung aufteilen). Außerdem werden keine Zwischenzeichenfolgen / -listen erstellt (Sie können Join entfernen und der Ausdruck wird faul, da jede Gruppe ein Iterator ist).


4

Anstatt eine re-Modulfunktion re.split zu verwenden, können Sie mit der Pandas-Methode series.str.split dasselbe Ergebnis erzielen.

Erstellen Sie zuerst eine Serie mit der obigen Zeichenfolge und wenden Sie dann die Methode auf die Serie an.

thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')

Der Parameter pat nimmt die Trennzeichen und gibt die geteilte Zeichenfolge als Array zurück. Hier werden die beiden Trennzeichen mit einem | übergeben (oder Betreiber). Die Ausgabe ist wie folgt:

[Hey, you , what are you doing here!?]


1
Es geht nicht um Ausführlichkeit, sondern darum, eine ganze Bibliothek (die ich übrigens liebe) zu importieren, um eine einfache Aufgabe auszuführen, nachdem eine Zeichenfolge in eine Panda-Serie konvertiert wurde. Nicht sehr "Occam freundlich".
Zar3bski

3

Ich mache mich wieder mit Python vertraut und brauchte das Gleiche. Die Lösung ist vielleicht besser, aber ich habe mir Folgendes ausgedacht:

tokens = [x.strip() for x in data.split(',')]

Clever, sollte an allen englischen grammatikalischen Konstrukten arbeiten, die mir einfallen, außer einem Bindestrich ohne Leerzeichen - zum Beispiel. (Problemumgehung.)
Ninjagecko

3

Mit Maketrans und Übersetzern können Sie dies einfach und ordentlich tun

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()

Tolle Antwort für Python> = 3.6
Revliscano

3

In Python 3 können Sie die Methode von PY4E - Python for Everybody verwenden .

Wir können beide diese Probleme lösen , indem sie die String - Methoden verwenden lower, punctuationund translate. Das translateist die subtilste der Methoden. Hier ist die Dokumentation für translate:

your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))

Ersetzen Sie die Zeichen fromstrdurch das Zeichen an derselben Position in tostrund löschen Sie alle Zeichen, die sich in befinden deletestr. Das fromstrund tostrkann eine leere Zeichenfolge sein und der deletestrParameter kann weggelassen werden.

Sie können die "Interpunktion" sehen:

In [10]: import string

In [11]: string.punctuation
Out[11]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'  

Für Ihr Beispiel:

In [12]: your_str = "Hey, you - what are you doing here!?"

In [13]: line = your_str.translate(your_str.maketrans('', '', string.punctuation))

In [14]: line = line.lower()

In [15]: words = line.split()

In [16]: print(words)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Weitere Informationen finden Sie unter:


2
Die Methoden translate () und maketrans () von Strings sind interessant, aber diese Methode kann nicht "an Trennzeichen (oder Leerzeichen) aufgeteilt" werden. Beispiel: "Es gab einen großen Einsturz" erzeugt stattdessen fälschlicherweise das Wort "Cavein" der erwarteten "Höhle" und "in" ... Dies macht also nicht das, was die Frage verlangt.
Eric O Lebigot

Genau wie das, was @EricLebigot kommentiert hat. Die obige Methode macht nicht das, was die Frage verlangt, sehr gut.
Jeremy Anifacc

2

Eine andere Möglichkeit, dies zu erreichen, ist die Verwendung des Natural Language Tool Kit ( nltk ).

import nltk
data= "Hey, you - what are you doing here!?"
word_tokens = nltk.tokenize.regexp_tokenize(data, r'\w+')
print word_tokens

Dies druckt: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Der größte Nachteil dieser Methode ist, dass Sie das nltk-Paket installieren müssen .

Die Vorteile sind, dass Sie mit dem Rest des nltk-Pakets eine Menge Spaß machen können, sobald Sie Ihre Token erhalten haben.


1

Erstens glaube ich nicht, dass Sie beabsichtigen, Interpunktion als Trennzeichen in den Teilungsfunktionen zu verwenden. Ihre Beschreibung legt nahe, dass Sie einfach Interpunktion aus den resultierenden Zeichenfolgen entfernen möchten.

Ich stoße ziemlich häufig darauf, und meine übliche Lösung erfordert keine erneute Lösung.

Einzeilige Lambda-Funktion mit Listenverständnis:

(erfordert import string):

split_without_punc = lambda text : [word.strip(string.punctuation) for word in 
    text.split() if word.strip(string.punctuation) != '']

# Call function
split_without_punc("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']


Funktion (traditionell)

Als traditionelle Funktion sind dies immer noch nur zwei Zeilen mit einem Listenverständnis (zusätzlich zu import string):

def split_without_punctuation2(text):

    # Split by whitespace
    words = text.split()

    # Strip punctuation from each word
    return [word.strip(ignore) for word in words if word.strip(ignore) != '']

split_without_punctuation2("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Es wird natürlich auch Kontraktionen und getrennte Wörter intakt lassen. Sie können text.replace("-", " ")Bindestriche vor dem Teilen immer in Leerzeichen umwandeln.

Allgemeine Funktion ohne Lambda oder Listenverständnis

Für eine allgemeinere Lösung (bei der Sie die zu eliminierenden Zeichen angeben können) und ohne Listenverständnis erhalten Sie:

def split_without(text: str, ignore: str) -> list:

    # Split by whitespace
    split_string = text.split()

    # Strip any characters in the ignore string, and ignore empty strings
    words = []
    for word in split_string:
        word = word.strip(ignore)
        if word != '':
            words.append(word)

    return words

# Situation-specific call to general function
import string
final_text = split_without("Hey, you - what are you doing?!", string.punctuation)
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Natürlich können Sie die Lambda-Funktion auch jederzeit auf eine bestimmte Zeichenfolge verallgemeinern.


1

Verwenden Sie zunächst immer re.compile (), bevor Sie eine RegEx-Operation in einer Schleife ausführen, da diese schneller als die normale Operation arbeitet.

Kompilieren Sie für Ihr Problem zuerst das Muster und führen Sie dann eine Aktion aus.

import re
DATA = "Hey, you - what are you doing here!?"
reg_tok = re.compile("[\w']+")
print reg_tok.findall(DATA)

1

Hier ist die Antwort mit einer Erklärung.

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

# replace all the non alpha-numeric with space and then join.
new_string = ''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])
# output of new_string
'Hey  you  what are you doing here  '

# str.split() will remove all the empty string if separator is not provided
new_list = new_string.split()

# output of new_list
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

# we can join it to get a complete string without any non alpha-numeric character
' '.join(new_list)
# output
'Hey you what are you doing'

oder in einer Zeile können wir Folgendes tun:

(''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])).split()

# output
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

aktualisierte Antwort


1

Erstellen Sie eine Funktion, die als Eingabe zwei Zeichenfolgen (die zu teilende Quellzeichenfolge und die Teilungszeichenfolge mit Trennzeichen) verwendet und eine Liste mit Teilwörtern ausgibt:

def split_string(source, splitlist):
    output = []  # output list of cleaned words
    atsplit = True
    for char in source:
        if char in splitlist:
            atsplit = True
        else:
            if atsplit:
                output.append(char)  # append new word after split
                atsplit = False
            else: 
                output[-1] = output[-1] + char  # continue copying characters until next split
    return output

1

Ich mag die Lösung von pprzemek, weil sie nicht davon ausgeht, dass die Trennzeichen einzelne Zeichen sind, und nicht versucht, einen regulären Ausdruck zu nutzen (was nicht gut funktionieren würde, wenn die Anzahl der Trennzeichen lang verrückt werden müsste).

Hier ist aus Gründen der Übersichtlichkeit eine besser lesbare Version der obigen Lösung:

def split_string_on_multiple_separators(input_string, separators):
    buffer = [input_string]
    for sep in separators:
        strings = buffer
        buffer = []  # reset the buffer
        for s in strings:
            buffer = buffer + s.split(sep)

    return buffer

0

habe das gleiche Problem wie @ooboo und finde dieses Thema @ ghostdog74 hat mich inspiriert, vielleicht findet jemand meine Lösung nützlich

str1='adj:sg:nom:m1.m2.m3:pos'
splitat=':.'
''.join([ s if s not in splitat else ' ' for s in str1]).split()

Geben Sie etwas an der Stelle ein und teilen Sie es mit demselben Zeichen, wenn Sie nicht an Stellen teilen möchten.


Was ist, wenn ich mit Wort teilen muss?
Harsha Biyani

0

Hier ist meine Entscheidung für eine Trennung mit mehreren Begrenzern:

def msplit( str, delims ):
  w = ''
  for z in str:
    if z not in delims:
        w += z
    else:
        if len(w) > 0 :
            yield w
        w = ''
  if len(w) > 0 :
    yield w

0

Ich denke, Folgendes ist die beste Antwort, um Ihren Anforderungen gerecht zu werden:

\W+ Vielleicht für diesen Fall geeignet, aber möglicherweise nicht für andere Fälle.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")

Ich stimme zu, die \wund \WLösungen sind keine Antwort auf (den Titel) der Frage. Beachten Sie, dass in Ihrer Antwort entfernt werden |sollte (Sie denken an expr0|expr1statt [char0 char1…]). Darüber hinaus ist compile()der reguläre Ausdruck nicht erforderlich .
Eric O Lebigot

0

Hier ist meine Meinung dazu ....

def split_string(source,splitlist):
    splits = frozenset(splitlist)
    l = []
    s1 = ""
    for c in source:
        if c in splits:
            if s1:
                l.append(s1)
                s1 = ""
        else:
            print s1
            s1 = s1 + c
    if s1:
        l.append(s1)
    return l

>>>out = split_string("First Name,Last Name,Street Address,City,State,Zip Code",",")
>>>print out
>>>['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']

0

Mir gefällt der replace()Weg am besten. Das folgende Verfahren ändert alle in einer Zeichenfolge definierten splitlistTrennzeichen in das erste Trennzeichen in splitlistund teilt dann den Text in dieses eine Trennzeichen auf. Es wird auch berücksichtigt, ob splitlistes sich um eine leere Zeichenfolge handelt. Es wird eine Liste von Wörtern ohne leere Zeichenfolgen zurückgegeben.

def split_string(text, splitlist):
    for sep in splitlist:
        text = text.replace(sep, splitlist[0])
    return filter(None, text.split(splitlist[0])) if splitlist else [text]

0
def get_words(s):
    l = []
    w = ''
    for c in s.lower():
        if c in '-!?,. ':
            if w != '': 
                l.append(w)
            w = ''
        else:
            w = w + c
    if w != '': 
        l.append(w)
    return l

Hier ist die Verwendung:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

0

Wenn Sie eine umkehrbare Operation wünschen (Trennzeichen beibehalten), können Sie diese Funktion verwenden:

def tokenizeSentence_Reversible(sentence):
    setOfDelimiters = ['.', ' ', ',', '*', ';', '!']
    listOfTokens = [sentence]

    for delimiter in setOfDelimiters:
        newListOfTokens = []
        for ind, token in enumerate(listOfTokens):
            ll = [([delimiter, w] if ind > 0 else [w]) for ind, w in enumerate(token.split(delimiter))]
            listOfTokens = [item for sublist in ll for item in sublist] # flattens.
            listOfTokens = filter(None, listOfTokens) # Removes empty tokens: ''
            newListOfTokens.extend(listOfTokens)

        listOfTokens = newListOfTokens

    return listOfTokens

0

Ich musste dies kürzlich tun, wollte aber eine Funktion, die etwas mit der Standardbibliotheksfunktion übereinstimmt. str.splitDiese Funktion verhält sich genauso wie die Standardbibliothek, wenn sie mit 0 oder 1 Argumenten aufgerufen wird.

def split_many(string, *separators):
    if len(separators) == 0:
        return string.split()
    if len(separators) > 1:
        table = {
            ord(separator): ord(separator[0])
            for separator in separators
        }
        string = string.translate(table)
    return string.split(separators[0])

HINWEIS : Diese Funktion ist nur nützlich, wenn Ihre Trennzeichen aus einem einzelnen Zeichen bestehen (wie in meinem Anwendungsfall).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.