Dividir cadenas en palabras con delimitadores de límite de palabras múltiples

671

Creo que lo que quiero hacer es una tarea bastante común, pero no he encontrado ninguna referencia en la web. Tengo texto con puntuación y quiero una lista de las palabras.

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

debiera ser

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

Pero Python str.split()solo funciona con un argumento, así que tengo todas las palabras con la puntuación después de dividirme con espacios en blanco. ¿Algunas ideas?

ooboo
fuente
66
Python str.split()también funciona sin argumentos en absoluto
Ivan Vinogradov

Respuestas:

468

Un caso donde las expresiones regulares están justificadas:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
RichieHindle
fuente
2
Gracias. Sin embargo, todavía estoy interesado: ¿cómo puedo implementar el algoritmo utilizado en este módulo? ¿Y por qué no aparece en el módulo de cadena?
ooboo
29
Las expresiones regulares pueden ser desalentadoras al principio, pero son muy poderosas. La expresión regular '\ w +' significa "un carácter de palabra (az, etc.) repetido una o más veces". Aquí hay un CÓMO sobre las expresiones regulares de Python: amk.ca/python/howto/regex
RichieHindle el
324
Esta no es la respuesta a la pregunta. Esta es una respuesta a una pregunta diferente, que resulta funcionar para esta situación particular. Es como si alguien preguntara "cómo hago un giro a la izquierda" y la respuesta más votada fue "tome los siguientes tres giros a la derecha". Funciona para ciertas intersecciones, pero no da la respuesta necesaria. Irónicamente, la respuesta está en re, pero no findall. La respuesta a continuación re.split()es superior.
Jesse Dhillon
44
@JesseDhillon "tomar todas las subcadenas que consisten en una secuencia de caracteres de palabras" y "dividir en todas las subcadenas que consisten en una secuencia de caracteres que no son palabras" son, literalmente, diferentes formas de expresar la misma operación; No estoy seguro de por qué llamarías una respuesta superior.
Mark Amery
44
@ TMWP: El apóstoles significa que una palabra como don'tse trata como una sola palabra, en lugar de dividirse en dony t.
RichieHindle
574

re.split ()

re.split (patrón, cadena [, maxsplit = 0])

División de cadena por las apariciones de patrón. Si se utilizan paréntesis de captura en el patrón, el texto de todos los grupos en el patrón también se devuelve como parte de la lista resultante. Si maxsplit no es cero, a lo sumo se producen divisiones maxsplit, y el resto de la cadena se devuelve como el elemento final de la lista. (Nota de incompatibilidad: en la versión original de Python 1.5, maxsplit fue ignorado. Esto se ha solucionado en versiones posteriores).

>>> 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.']
gimel
fuente
13
Esta solución tiene la ventaja de adaptarse fácilmente para dividirse en guiones bajos, algo que la solución findall no hace: print re.split ("\ W + | _", "Testing this_thing") 'produce: [' Testing ',' this ' , 'thing']
Emil Stenström
63
Ahora bien, si sólo pudiera recordar la diferencia entre \w, \W, \s, y \S. Quien haya pensado que la capitalización de una bandera debería invertir su significado debe recibir un disparo en la cabeza.
ArtOfWarfare
1
Un caso de uso común de la división de cadenas es eliminar las entradas de cadenas vacías del resultado final. ¿Es posible hacer eso con este método? re.split ('\ W +', 'abc') da como resultado ['', 'a', 'b', 'c', '']
Scott Morken
3
@ArtOfWarfare Es común usar la shifttecla para hacer lo contrario de algo. ctrl+zdeshacer vs. ctrl+shift+zpara rehacer. Entonces shift w, o W, sería lo contrario de w.
Frank Vel
1
Esta respuesta debe estar en la parte superior: es la única que responde con precisión el título de la pregunta.
Kranach
381

Otra forma rápida de hacer esto sin una expresión regular es reemplazar primero los caracteres, como se muestra a continuación:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']
Louis LC
fuente
71
Rápido y sucio pero perfecto para mi caso (mis separadores eran un conjunto pequeño y conocido)
Andy Baker
77
Perfecto para el caso en el que no tiene acceso a la biblioteca RE, como ciertos microcontroladores pequeños. :-)
tu-Reinstate Monica-dor duh
11
Creo que esto es más explícito que RE también, por lo que es un poco amigable para los novatos. A veces no necesito una solución general para todo
Adam Hughes
Increíble. Tenía un .split () en una situación de entrada múltiple, y necesitaba detectar cuándo el usuario, yo, separaba las entradas con un espacio y no una coma. Estaba a punto de rendirme y relanzar con re, pero su solución .replace () dio en el clavo. Gracias.
JayJay123
obtendrá una respuesta incorrecta cuando no desee dividir en espacios y desee dividir en otros personajes.
Ahmed Amr
307

Tantas respuestas, sin embargo, no puedo encontrar ninguna solución que haga eficientemente lo que literalmente pide el título de las preguntas (división en múltiples separadores posibles; en cambio, muchas respuestas se dividen en cualquier cosa que no sea una palabra, que es diferente). Así que aquí hay una respuesta a la pregunta en el título, que se basa en el remódulo estándar y eficiente de Python :

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

dónde:

  • los […]partidos uno de los separadores enumerados en el interior,
  • el \-en la expresión regular está aquí para evitar la interpretación especial de -como un indicador de rango de caracteres (como en A-Z),
  • los +saltos de uno o más delimitadores (que podrían omitirse gracias a lafilter() , pero esto produciría innecesariamente cadenas vacías entre separadores emparejados), y
  • filter(None, …) elimina las cadenas vacías posiblemente creadas por los separadores iniciales y finales (ya que las cadenas vacías tienen un valor booleano falso).

Esto re.split()precisamente "se divide con separadores múltiples", como se solicitó en el título de la pregunta.

Además, esta solución es inmune a los problemas con caracteres que no son ASCII en las palabras que se encuentran en otras soluciones (vea el primer comentario a la respuesta de ghostdog74 ).

¡El remódulo es mucho más eficiente (en velocidad y concisión) que hacer bucles y pruebas de Python "a mano"!

Eric O Lebigot
fuente
3
"No puedo encontrar ninguna solución que haga eficientemente lo que literalmente pregunta el título de las preguntas". La segunda respuesta hace eso, publicada hace 5 años: stackoverflow.com/a/1059601/2642204 .
BartoszKP
17
Esta respuesta no se divide en delimitadores (de un conjunto de delimitadores múltiples): en cambio, se divide en cualquier cosa que no sea alfanumérica. Dicho esto, estoy de acuerdo en que la intención del póster original es probablemente mantener solo las palabras, en lugar de eliminar algunos signos de puntuación.
Eric O Lebigot
EOL: Creo que esta respuesta se divide en un conjunto de delimitadores múltiples. Si agrega caracteres no alfanuméricos a la cadena que no se especifican, como el guión bajo, no se dividen, como se esperaba.
GravityWell
@GravityWell: No estoy seguro de entender: ¿puede dar un ejemplo concreto?
Eric O Lebigot
3
@EOL: Me acabo de dar cuenta de que estaba confundido por su comentario "Esta respuesta no se divide ..." Pensé que "esto" se refería a su respuesta re.split, pero ahora me doy cuenta de que se refería a la respuesta de Gimel. Creo que ESTA respuesta (la respuesta a la que estoy comentando) es la mejor respuesta :)
GravityWell
56

Otra forma, sin expresiones regulares

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()
ghostdog74
fuente
8
Esta solución es en realidad mejor que la aceptada. Funciona sin caracteres ASCII, inténtalo "Hey, you - what are you doing here María!?". La solución aceptada no funcionará con el ejemplo anterior.
Christopher Ramírez
44
Creo que hay un pequeño problema aquí ... Su código agregará caracteres que están separados con signos de puntuación y, por lo tanto, no los dividirá ... Si no me equivoco, su última línea debería ser:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu
Se puede hacer que la biblioteca de expresiones regulares acepte convenciones Unicode para caracteres si es necesario. Además, tiene el mismo problema que solía tener la solución aceptada: como lo es ahora, se divide en apóstrofes. Es posible que desee o for o in s if (o in not string.punctuation or o == "'"), pero luego se está volviendo demasiado complicado para una sola línea si agregamos también el parche de cedbeu.
Daniel H
Hay otro problema aquí. Incluso cuando tenemos en cuenta los cambios de @cedbeu, este código no funciona si la cadena es algo así "First Name,Last Name,Street Address,City,State,Zip Code"y queremos dividir solo en una coma ,. La salida deseada sería: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Lo que obtenemos en su lugar:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker
44
Esta solución es terriblemente ineficiente: primero la lista se deconstruye en caracteres individuales, luego se revisa todo el conjunto de caracteres de puntuación para cada uno de los caracteres en la cadena original, luego los caracteres se vuelven a ensamblar y luego se vuelven a dividir. Todo este "movimiento" también es muy complicado, en comparación con una solución basada en expresiones regulares: incluso si la velocidad no importa en una aplicación dada, no hay necesidad de una solución complicada. Como el remódulo es estándar y ofrece legibilidad y velocidad, no veo por qué debería evitarse.
Eric O Lebigot
39

Pro-Tip: uso string.translate para las operaciones de cadena más rápidas que tiene Python.

Alguna prueba ...

Primero, el camino lento (lo siento 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

A continuación, usamos re.findall()(como se indica en la respuesta sugerida). Mucho mas rápido:

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

Finalmente, usamos 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

Explicación:

string.translatese implementa en C y, a diferencia de muchas funciones de manipulación de cadenas en Python, string.translate no produce una nueva cadena. Por lo tanto, es lo más rápido posible para la sustitución de cadenas.

Sin embargo, es un poco incómodo, ya que necesita una tabla de traducción para hacer esta magia. Puede hacer una tabla de traducción con la maketrans()función de conveniencia. El objetivo aquí es traducir todos los caracteres no deseados a espacios. Un sustituto uno por uno. Nuevamente, no se producen nuevos datos. Entonces esto es rápido !

A continuación, usamos buenos viejos split(). split()de manera predeterminada, funcionará en todos los caracteres de espacios en blanco, agrupándolos para la división. El resultado será la lista de palabras que desea. ¡Y este enfoque es casi 4 veces más rápido que re.findall()!

Dave
fuente
44
Hice una prueba aquí, y si necesita usar Unicode, usarlo patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)es más rápido que traducir, porque debe codificar la cadena antes de aplicar la transformación y decodificar cada elemento de la lista después de la división para volver a Unicode.
Rafael S. Calsaverini
Puede unir la implementación de traducción y asegurarse de que S no esté entre los divisores con:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
hobs
Ninguna toma. Estás comparando manzanas y naranjas. ;) mi solución en python 3 todavía funciona; P y tiene soporte para separadores multi-char. :) intente hacerlo de manera simple sin asignar una nueva cadena. :) pero cierto, el mío se limita a analizar parámetros de línea de comandos y no a un libro, por ejemplo.
pprzemek
dices "no produce una nueva cadena", lo que significa que funciona in situ en una cadena determinada? Lo probé ahora con python 2.7 y no modifica la cadena original y devuelve una nueva.
Prokop Hapala
26

Tenía un dilema similar y no quería usar el módulo 're'.

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']
pprzemek
fuente
1
Me gusta esto. Solo una nota, el orden de los separadores es importante. Lo siento si eso es obvio.
crizCraig
2
¿Por qué no usar el remódulo, que es más rápido y más claro (no es que las expresiones regulares sean especialmente claras, sino porque es mucho más corto y directo)?
Eric O Lebigot
13

Primero, quiero estar de acuerdo con los demás en que la expresión regular o las str.translate(...)soluciones basadas son más eficaces. Para mi caso de uso, el rendimiento de esta función no fue significativo, por lo que quería agregar ideas que consideraba con ese criterio.

Mi objetivo principal era generalizar las ideas de algunas de las otras respuestas en una solución que pudiera funcionar para cadenas que contengan más que palabras de expresión regular (es decir, poner en una lista negra el subconjunto explícito de caracteres de puntuación frente a caracteres de palabras en la lista blanca).

Tenga en cuenta que, en cualquier enfoque, uno también podría considerar el uso string.punctuationen lugar de una lista definida manualmente.

Opción 1 - re.sub

Me sorprendió ver que no hay respuesta hasta ahora utiliza re.sub (...) . Me parece un enfoque simple y natural para este problema.

import re

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

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

En esta solución, anidé la llamada al re.sub(...)interior re.split(...), pero si el rendimiento es crítico, compilar la expresión regular en el exterior podría ser beneficioso, para mi caso de uso, la diferencia no fue significativa, por lo que prefiero la simplicidad y la legibilidad.

Opción 2 - reemplazo de str.

Estas son algunas líneas más, pero tiene la ventaja de ser expansible sin tener que verificar si necesita escapar de cierto carácter en expresiones regulares.

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

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

words = my_str.split()

Hubiera sido agradable poder asignar el str.replace a la cadena en su lugar, pero no creo que se pueda hacer con cadenas inmutables, y aunque el mapeo contra una lista de caracteres funcionaría, ejecutar cada reemplazo contra cada carácter Suena excesivo. (Editar: consulte la siguiente opción para ver un ejemplo funcional).

Opción 3 - functools.reduce

(En Python 2, reduceestá disponible en el espacio de nombres global sin importarlo desde functools).

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()
Taylor Edmiston
fuente
Hm, se debe usar otro método str.translate: no es compatible con Unicode, pero es más rápido que otros métodos y, como tal, podría ser bueno en algunos casos: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))también aquí es obligatorio tener reemplazos como una cadena de caracteres, no tuplas o lista.
MarSoft
@MarSoft ¡Gracias! Mencioné eso en la parte superior de la respuesta, pero decidí no agregarlo ya que las respuestas existentes ya lo discutieron bien.
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]

Entonces esto se convierte en un trazador de líneas:

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

Explicación

Esto es lo que en Haskell se conoce como la mónada Lista. La idea detrás de la mónada es que una vez "en la mónada" usted "permanece en la mónada" hasta que algo lo saque. Por ejemplo, en Haskell, supongamos que asigna la range(n) -> [1,2,...,n]función de Python sobre una Lista. Si el resultado es una Lista, se agregará a la Lista en el lugar, por lo que obtendría algo así map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Esto se conoce como map-append (o mappend, o tal vez algo así). La idea aquí es que tienes esta operación que estás aplicando (dividiendo en un token), y cada vez que haces eso, unes el resultado en la lista.

Puede abstraer esto en una función y tener tokens=string.punctuationpor defecto.

Ventajas de este enfoque:

  • Este enfoque (a diferencia de los enfoques ingenuos basados ​​en expresiones regulares) puede funcionar con tokens de longitud arbitraria (que la expresión regular también puede hacer con una sintaxis más avanzada).
  • No está restringido a simples fichas; podría tener una lógica arbitraria en lugar de cada token, por ejemplo, uno de los "tokens" podría ser una función que se divide de acuerdo con la forma en que se anidan los paréntesis.
ninjagecko
fuente
Solución ordenada de Haskell, pero en mi opinión, esto se puede escribir más claramente sin mappend en Python.
Vlad the Impala
@Goose: el punto era que la función de 2 líneas map_then_appendse puede usar para hacer que un problema sea de 2 líneas, así como muchos otros problemas mucho más fáciles de escribir. La mayoría de las otras soluciones usan el remódulo de expresión regular , que no es python. Pero he estado descontento con la forma en que hago mi respuesta parece poco elegante y bloaty cuando está muy concisa ... voy a editarlo ...
ninjagecko
¿Se supone que esto funciona en Python tal como está escrito? mi fragmentsresultado es solo una lista de los caracteres de la cadena (incluidos los tokens).
Rick apoya a Mónica
@RickTeachey: funciona para mí en python2 y python3.
ninjagecko
hmmmm Quizás el ejemplo es un poco ambiguo. He probado el código en la respuesta a todo tipo de diferentes maneras- incluyendo el tener fragments = ['the,string'], fragments = 'the,string'o fragments = list('the,string')ninguno de ellos están produciendo la salida derecha.
Rick apoya a Monica
5

prueba esto:

import re

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

esto imprimirá ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Corey Goldberg
fuente
4

Use reemplazar dos veces:

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

resultados en:

['11223', '33344', '33222', '3344']
jeroen
fuente
4

Me gusta re , pero aquí está mi solución sin ella:

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 .__ contiene__ es un método utilizado por el operador 'in'. Básicamente es lo mismo que

lambda ch: ch in sep

Pero es más conveniente aquí.

groupby obtiene nuestra cadena y función. Divide la cadena en grupos usando esa función: cada vez que cambia un valor de función, se genera un nuevo grupo. Entonces, sep .__ contiene__ es exactamente lo que necesitamos.

groupby devuelve una secuencia de pares, donde pair [0] es el resultado de nuestra función y pair [1] es un grupo. Usando 'si no k' filtramos los grupos con separadores (porque un resultado de sep .__ contiene__ es verdadero en los separadores). Bueno, eso es todo: ahora tenemos una secuencia de grupos donde cada uno es una palabra (el grupo es en realidad un iterable, por lo que usamos join para convertirlo en cadena).

Esta solución es bastante general, ya que utiliza una función para separar cadenas (puede dividirlas por cualquier condición que necesite). Además, no crea cadenas / listas intermedias (puede eliminar la unión y la expresión se volverá perezosa, ya que cada grupo es un iterador)

monitorius
fuente
4

En lugar de utilizar una función re module re.split, puede lograr el mismo resultado utilizando el método de pandas series.str.split.

Primero, cree una serie con la cadena anterior y luego aplique el método a la serie.

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

El parámetro pat toma los delimitadores y devuelve la cadena dividida como una matriz. Aquí los dos delimitadores se pasan usando un | (u operador). La salida es la siguiente:

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

Tarun Kumar Yellapu
fuente
1
No es una cuestión detallada, sino el hecho de importar una biblioteca completa (que me encanta, por cierto) para realizar una tarea simple después de convertir una cadena en una serie de panda. No muy "Occam amigable".
zar3bski
3

Me estoy reencontrando con Python y necesitaba lo mismo. La solución Findall puede ser mejor, pero se me ocurrió esto:

tokens = [x.strip() for x in data.split(',')]
Leon Starr
fuente
Inteligente, debería funcionar en todas las construcciones gramaticales en inglés que se me ocurran, excepto un guión sin espacios, esto, por ejemplo. (Solución).
ninjagecko
3

usando maketrans y traducir puedes hacerlo de manera fácil y ordenada

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
Ritesh Sinha
fuente
Gran respuesta en cuanto a Python> = 3.6
revliscano
3

En Python 3, puede usar el método de PY4E - Python para todos .

Podemos resolver estos dos problemas mediante el uso de los métodos de las cadenas lower, punctuationy translate. El translatees el más sutil de los métodos. Aquí está la documentación para translate:

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

Reemplace los caracteres fromstrcon el carácter en la misma posición tostry elimine todos los caracteres que están en deletestr. El fromstry tostrpuede ser cadenas vacías y el deletestrparámetro puede omitirse.

Puedes ver la "puntuación":

In [10]: import string

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

Por su ejemplo:

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

Para más información, puede consultar:

Jeremy Anifacc
fuente
2
Los métodos de cadenas translate () y maketrans () son interesantes, pero este método no se "divide en delimitadores" (o espacios en blanco): por ejemplo, "Hubo un gran hundimiento" producirá incorrectamente la palabra "cavein" de la esperada "cueva" y "en" ... Por lo tanto, esto no hace lo que la pregunta pide.
Eric O Lebigot
Justo como lo que comentó @EricLebigot. El método anterior no hace muy bien lo que pide la pregunta.
Jeremy Anifacc
2

Otra forma de lograr esto es usar el Kit de herramientas de lenguaje natural ( nltk ).

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

Esto imprime: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

El mayor inconveniente de este método es que necesita instalar el paquete nltk .

Los beneficios son que puedes hacer muchas cosas divertidas con el resto del paquete nltk una vez que obtengas tus tokens.

tgray
fuente
1

En primer lugar, no creo que su intención sea utilizar la puntuación como delimitadores en las funciones divididas. Su descripción sugiere que simplemente desea eliminar la puntuación de las cadenas resultantes.

Me encuentro con bastante frecuencia, y mi solución habitual no requiere re.

Función lambda de una línea con comprensión de lista:

(requiere 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']


Función (tradicional)

Como función tradicional, esto sigue siendo solo dos líneas con una comprensión de la lista (además de 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']

Naturalmente, también dejará intactas las contracciones y las palabras con guiones. Siempre puede usar text.replace("-", " ")para convertir guiones en espacios antes de la división.

Función general sin Lambda o comprensión de lista

Para una solución más general (donde puede especificar los caracteres a eliminar), y sin una comprensión de la lista, obtiene:

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

Por supuesto, siempre puede generalizar la función lambda a cualquier cadena de caracteres especificada también.

cosmicFluke
fuente
1

En primer lugar, use siempre re.compile () antes de realizar cualquier operación RegEx en un bucle porque funciona más rápido que la operación normal.

entonces, para su problema, primero compile el patrón y luego realice una acción sobre él.

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

Aquí está la respuesta con alguna explicación.

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'

o en una línea, podemos hacer así:

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

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

respuesta actualizada

Tasneem Haider
fuente
1

Cree una función que tome como entrada dos cadenas (la cadena fuente que se dividirá y la cadena de delimitadores de la lista dividida) y generará una lista de palabras divididas:

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
user852006
fuente
1

Me gusta la solución de pprzemek porque no asume que los delimitadores son caracteres individuales y no trata de aprovechar una expresión regular (que no funcionaría bien si el número de separadores llegara a ser muy largo).

Aquí hay una versión más legible de la solución anterior para mayor claridad:

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
Everett
fuente
0

tengo el mismo problema que @ooboo y encuentro este tema @ ghostdog74 me inspiró, tal vez alguien encuentre útil mi solución

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

ingrese algo en el lugar del espacio y divídalo con el mismo carácter si no desea dividirlo en los espacios.

badas
fuente
¿Qué pasa si tengo que dividir usando Word?
Harsha Biyani
0

Aquí está mi ir a una división con múltiples deliminadores:

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
Martlark
fuente
0

Creo que la siguiente es la mejor respuesta para satisfacer sus necesidades:

\W+ puede ser adecuado para este caso, pero puede no serlo para otros casos.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")
nemozhp
fuente
Estoy de acuerdo, las soluciones \wy \Wno son una respuesta a (el título de) la pregunta. Tenga en cuenta que en su respuesta, |debe eliminarse (está pensando en expr0|expr1lugar de [char0 char1…]). Además, no hay necesidad de compile()la expresión regular.
Eric O Lebigot
0

Aquí está mi opinión sobre esto ...

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']
Arindam Roychowdhury
fuente
0

Me gusta la replace()forma lo mejor. El siguiente procedimiento cambia todos los separadores definidos en una cadena splitlistal primer separador splitlisty luego divide el texto en ese separador. También tiene en cuenta si splitlistresulta ser una cadena vacía. Devuelve una lista de palabras, sin cadenas vacías.

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]
Stefan van den Akker
fuente
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

Aquí está el uso:

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

Si desea una operación reversible (preservar los delimitadores), puede usar esta función:

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
Nadav B
fuente
0

Hace poco necesitaba hacer esto, pero quería una función que coincidiera con la str.splitfunción de biblioteca estándar , esta función se comporta igual que la biblioteca estándar cuando se llama con 0 o 1 argumentos.

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

NOTA : Esta función solo es útil cuando los separadores consisten en un solo carácter (como fue mi caso de uso).

Justin Fay
fuente