2008-12-14 15 views
38

Ich versuche, eine Zeichenfolge in Wörter und Interpunktion aufzuteilen, indem ich die Interpunktion der Liste hinzufüge, die durch die Aufteilung erzeugt wurde.Eine Zeichenfolge in Wörter und Satzzeichen aufteilen

Zum Beispiel:

>>> c = "help, me" 
>>> print c.split() 
['help,', 'me'] 

Was ich wirklich die Liste aussehen soll ist:

['help', ',', 'me'] 

Also, ich möchte bei Leerzeichen die Zeichenfolge Spaltung mit der Interpunktion Split aus den Worten.

Ich habe versucht, die Zeichenfolge zuerst zu analysieren und dann die geteilte:

>>> for character in c: 
...  if character in ".,;!?": 
...    outputCharacter = " %s" % character 
...  else: 
...    outputCharacter = character 
...  separatedPunctuation += outputCharacter 
>>> print separatedPunctuation 
help , me 
>>> print separatedPunctuation.split() 
['help', ',', 'me'] 

Dies erzeugt das Ergebnis, das ich will, aber es ist sehr langsam auf große Dateien.

Gibt es eine Möglichkeit, dies effizienter zu tun?

+0

Für dieses Beispiel (nicht der allgemeine Fall) 'c.replace (' ' '') Partition (',')' –

Antwort

57

Dies ist mehr oder weniger die Art und Weise, es zu tun:

>>> import re 
>>> re.findall(r"[\w']+|[.,!?;]", "Hello, I'm a string!") 
['Hello', ',', "I'm", 'a', 'string', '!'] 

Der Trick ist, nicht daran zu denken, wo die Zeichenfolge zu spalten, aber was in den Token enthalten.

Caveats:

  • Der Unterstrich (_) ist ein innerWortZeichen betrachtet. Ersetzen Sie \ w, wenn Sie das nicht wollen.
  • Dies funktioniert nicht mit (einzelnen) Anführungszeichen in der Zeichenfolge.
  • Setzen Sie zusätzliche Interpunktionszeichen, die Sie in der rechten Hälfte des regulären Ausdrucks verwenden möchten.
  • Alles was nicht explizit erwähnt wird, wird stillgelegt.
+0

Danke, funktioniert perfekt. –

+2

Wenn Sie bei jeder Interpunktion, einschließlich '' ', aufteilen möchten, versuchen Sie' re.findall (r "[\ w] + | [^ \ s \ w]", "Hallo, ich bin ein String!") ' . Das Ergebnis ist '['Hallo', ',', 'I', '' ',' m ',' a ',' string ','! ']' Beachten Sie auch, dass Ziffern in der Wortübereinstimmung enthalten sind. –

+0

Entschuldigung! Könntest du erklären, wie genau das funktioniert? – Curious

-1

Haben Sie versucht, eine Regex zu verwenden?

http://docs.python.org/library/re.html#re-syntax


Durch die Art und Weise. Warum brauchst du das "," beim zweiten? Sie werden wissen, dass nach jedem Text geschrieben dh

[0]

""

[1]

""

Also, wenn Sie die "hinzufügen möchten , "Sie können es einfach nach jeder Iteration tun, wenn Sie das Array verwenden.

4

In Perl-Stil regulären Ausdruck Syntax passt \b eine Wortgrenze. Dies sollte für eine Regex-basierte Aufteilung nützlich sein.

bearbeiten: Ich wurde von hop informiert, dass "leere Übereinstimmungen" nicht in der Split-Funktion von Pythons re-Modul funktionieren. Ich werde das hier als Information für jeden anderen verlassen, der von diesem "Feature" überrumpelt wird.

+0

nur nicht, weil re.split nicht mit r '\ b' funktioniert ... – hop

+0

Was zur Hölle? Ist das ein Fehler in re.split? In Perl funktioniert 'split/\ b \ s * /' ohne Probleme. – Svante

+0

es ist irgendwie dokumentiert, dass re.split() wird nicht auf leere Übereinstimmungen aufgeteilt ... so, nein, nicht/wirklich/ein Fehler. – hop

0

Ich denke, Sie können alle Hilfe, die Sie sich vorstellen können in der NLTK, vor allem, da Sie Python verwenden. Es gibt eine gute umfassende Diskussion dieses Problems im Tutorial.

1

Hier ist ein kleines Update für Ihre Implementierung. Wenn Sie versuchen, etwas detaillierter zu machen, schlage ich vor, in die NLTK zu schauen, die le dorfer vorgeschlagen hat.

Dies könnte nur ein wenig schneller sein, da '' .join() anstelle von + = verwendet wird, was known to be faster ist.

import string 

d = "Hello, I'm a string!" 

result = [] 
word = '' 

for char in d: 
    if char not in string.whitespace: 
     if char not in string.ascii_letters + "'": 
      if word: 
        result.append(word) 
      result.append(char) 
      word = '' 
     else: 
      word = ''.join([word,char]) 

    else: 
     if word: 
      result.append(word) 
      word = '' 
print result 
['Hello', ',', "I'm", 'a', 'string', '!'] 
+0

Ich habe dies nicht profiliert, aber ich denke, das Hauptproblem liegt in der char-by-char Verkettung von Wörtern. Ich würde stattdessen einen Index und Slices verwenden. – hop

+0

Mit Tricks kann ich die Ausführungszeit Ihrer Lösung um 50% reduzieren. Meine Lösung mit re.findall() ist immer noch doppelt so schnell. – hop

+1

Sie müssen 'if word: result.append (word)' nach dem Ende der Schleife aufrufen, sonst ist das letzte Wort nicht im Ergebnis. –

2

Hier ist mein Eintrag.

Ich habe meine Zweifel, wie gut dies im Sinne von Effizienz halten wird, oder ob es alle Fälle erfasst (beachten Sie die "!!!" zusammen gruppiert; dies kann oder kann nicht eine gute Sache sein).

>>> import re 
>>> import string 
>>> s = "Helo, my name is Joe! and i live!!! in a button; factory:" 
>>> l = [item for item in map(string.strip, re.split("(\W+)", s)) if len(item) > 0] 
>>> l 
['Helo', ',', 'my', 'name', 'is', 'Joe', '!', 'and', 'i', 'live', '!!!', 'in', 'a', 'button', ';', 'factory', ':'] 
>>> 

Eine offensichtliche Optimierung wäre den Regex vor der Hand (mit re.compile) zu kompilieren, wenn Sie vorhaben, diese Basis auf einer Zeile-für-Zeile zu tun.

22

Hier ist eine Unicode-fähige Version:

re.findall(r"\w+|[^\w\s]", text, re.UNICODE) 

Die erste Alternative fängt Sequenzen von Wortzeichen (wie durch Unicode definiert, so "Resume" wird sich nicht in ['r', 'sum']); Die zweite fängt einzelne Nicht-Wort-Zeichen ab und ignoriert Whitespace.

Beachten Sie, dass im Gegensatz zur oberen Antwort das Anführungszeichen als separate Interpunktion behandelt wird (z. B. "Ich bin" ->['I', "'", 'm']). Dies scheint in NLP Standard zu sein, daher halte ich das für ein Feature.

+0

Upvoted weil das '\ w + | [^ \ w \ s]' Konstrukt generischer ist als die angenommene Antwort aber afaik in Python 3 sollte der re.UNICODE nicht notwendig sein – rloth

0

kam ich mit einer Art und Weise auf alle Wörter und \W+ Muster tokenize \b verwendet, die keine Hardcoding benötigt:

>>> import re 
>>> sentence = 'Hello, world!' 
>>> tokens = [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', sentence)] 
['Hello', ',', 'world', '!'] 

Hier .*?\S.*? ist ein Muster, alles passend, dass kein Raum ist und $ hinzugefügt Passen Sie das letzte Token in einer Zeichenfolge an, wenn es sich um ein Interpunktionssymbol handelt.

Beachten Sie die folgenden aber - dieser Wille Gruppe Zeichensetzung, die von mehr als einem Symbol besteht:

>>> for token in [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', '"You can", she said')]: 
...  print re.findall(r'(?:\w+|\W)', token) 

['You'] 
['can'] 
['"', ','] 
['she'] 
['said'] 
0

Versuchen Sie folgendes:

>>> print [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', '"Oh no", she said')] 
['Oh', 'no', '",', 'she', 'said'] 

Natürlich können Sie solche Gruppen mit finden und Split :.

string_big = "One of Python's coolest features is the string format operator This operator is unique to strings" 
my_list =[] 
x = len(string_big) 
poistion_ofspace = 0 
while poistion_ofspace < x: 
    for i in range(poistion_ofspace,x): 
     if string_big[i] == ' ': 
      break 
     else: 
      continue 
    print string_big[poistion_ofspace:(i+1)] 
    my_list.append(string_big[poistion_ofspace:(i+1)]) 
    poistion_ofspace = i+1 

print my_list 
Verwandte Themen