2008-12-14 20 views
38

Estoy tratando de dividir una cadena en palabras y signos de puntuación, agregando la puntuación a la lista producida por la división.Dividir una cadena en palabras y puntuación

Por ejemplo:

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

Lo que realmente quiero la lista para que parezca es:

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

lo tanto, quiero la división cadena en los espacios en blanco con la división de puntuacion de las palabras.

He tratado de analizar la cadena primero y luego ejecutar la división:

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

Esto produce el resultado que quiero, pero es muy lento en archivos grandes.

¿Hay alguna manera de hacer esto de manera más eficiente?

+0

Para este ejemplo (no el caso general) 'c.replace (' ' '') partición (',')' –

Respuesta

57

Esto es más o menos la forma de hacerlo:

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

El truco es, no pensar en dónde dividir la cadena, pero lo que debe incluir en las fichas.

Advertencias:

  • el subrayado (_) se considera un carácter interior-palabra. Reemplace \ w, si no quiere eso.
  • Esto no funcionará con comillas (únicas) en la cadena.
  • Ponga los signos de puntuación adicionales que quiera usar en la mitad derecha de la expresión regular.
  • Todo lo que no se mencione explícitamente en la re se quita silenciosamente.
+0

Gracias, funciona a la perfección. –

+2

Si desea dividir en CUALQUIER puntuación, incluyendo ''', intente 're.findall (r" [\ w] + | [^ \ s \ w] "," ¡Hola, soy una cadena! ")' . El resultado es '['Hola', ',', 'I'," '",' m ',' a ',' cadena ','! ']' Tenga en cuenta también que los dígitos están incluidos en la palabra coincidencia. –

+0

¡Lo siento! ¿Podría explicarme cómo funciona esto exactamente? – Curious

-1

¿Has probado usar una expresión regular?

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


Por cierto. ¿Por qué necesitas el "," en el segundo? Usted sabrá que después de cada texto está escrito es decir

[0]

""

[1]

""

Así que si desea agregar el " , "puede hacerlo después de cada iteración cuando usa la matriz ...

4

En la sintaxis de expresiones regulares perl-style, \b coincide con un límite de palabras. Esto debería ser útil para hacer una división basada en expresiones regulares.

editar: Me han informado por hop que "coincidencias vacías" no funcionan en la función de división del módulo de réplica de Python. Dejaré esto aquí como información para que cualquier otra persona quede perpleja por esta "característica".

+0

solo no funciona porque re.split no funcionará con r '\ b' ... – hop

+0

¿Qué diablos? ¿Es eso un error en re.split? En Perl, 'split/\ b \ s * /' funciona sin ningún problema. – Svante

+0

está documentado que re.split() no se dividirá en las coincidencias vacías ... entonces, no, no/realmente/un error. – hop

0

Creo que puedes encontrar toda la ayuda que puedas imaginar en el NLTK, especialmente porque estás usando python. Hay una buena discusión exhaustiva sobre este tema en el tutorial.

1

Aquí hay una actualización menor de su implementación. Si estás tratando de hacer algo más detallado, te sugiero que busques en el NLTK lo que sugirió le dorfier.

Esto podría ser solo un poco más rápido ya que '' .join() se usa en lugar de + =, que es known to be faster.

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

no he perfilado esto, pero supongo que el problema principal es con la concatenación de palabra por palabra. en cambio, usaría un índice y rebanadas. – hop

+0

Con trucos puedo afeitar el 50% de descuento en el tiempo de ejecución de su solución. mi solución con re.findall() sigue siendo dos veces más rápida. – hop

+1

Debe llamar 'if word: result.append (word)' después de que el ciclo finalice, de lo contrario, la última palabra no está en el resultado. –

2

Aquí está mi entrada.

Tengo mis dudas en cuanto a qué tan bien se mantendrá en el sentido de eficiencia, o si atrapa todos los casos (tenga en cuenta el "!!!" agrupados, esto puede o no ser algo bueno).

>>> 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', ':'] 
>>> 

Una optimización obvia sería para compilar la expresión regular antes de la mano (usando re.compile) si usted va a estar haciendo esto sobre una base de línea por línea.

22

Aquí es una versión Unicode-aware:

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

La primera alternativa atrapa secuencias de caracteres de palabra (como se define por Unicode, de modo "RESUME" no se convertirá en ['r', 'sum']); el segundo atrapa caracteres individuales sin palabra, ignorando el espacio en blanco.

Tenga en cuenta que, a diferencia de la respuesta superior, esto trata la comilla simple como puntuación separada (por ejemplo, "Soy" ->['I', "'", 'm']). Esto parece ser estándar en PNL, así que lo considero una característica.

+0

Upvoted porque el constructo '\ w + | [^ \ w \ s]' es más genérico que la respuesta aceptada pero afaik en python 3 el re.UNICODE no debería ser necesario – rloth

0

me ocurrió una manera de tokenize todas las palabras y \W+ patrones usando \b que no necesitan codificar:

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

Aquí .*?\S.*? es un patrón de coincidencia nada que no sea un espacio y se añade a $ Haga coincidir el último token en una cadena si es un símbolo de puntuación.

Nota lo siguiente, aunque - este grupo de puntuacion voluntad que consiste en más de un símbolo:

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

Por supuesto, usted puede encontrar y dividir estos grupos con:

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

Prueba esto :.

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 
Cuestiones relacionadas