Fractionner les chaînes en mots avec plusieurs délimiteurs de limites de mots

671

Je pense que ce que je veux faire est une tâche assez courante mais je n'ai trouvé aucune référence sur le web. J'ai un texte avec ponctuation et je veux une liste des mots.

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

devrait être

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

Mais Python str.split()ne fonctionne qu'avec un seul argument, j'ai donc tous les mots avec la ponctuation après que je me suis séparé avec des espaces. Des idées?

ooboo
la source
6
python str.split()fonctionne également sans aucun argument
Ivan Vinogradov

Réponses:

468

Un cas où les expressions régulières sont justifiées:

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
la source
2
Merci. Toujours intéressé, cependant - comment puis-je implémenter l'algorithme utilisé dans ce module? Et pourquoi n'apparaît-il pas dans le module chaîne?
ooboo
29
Les expressions régulières peuvent être intimidantes au début, mais elles sont très puissantes. L'expression régulière '\ w +' signifie "un caractère de mot (az etc.) répété une ou plusieurs fois". Il y a un HOWTO sur les expressions régulières Python ici: amk.ca/python/howto/regex
RichieHindle
324
Ce n'est pas la réponse à la question. Ceci est une réponse à une question différente, qui se produit pour cette situation particulière. C'est comme si quelqu'un demandait «comment faire un virage à gauche» et la réponse la plus votée était «prenez les trois prochains virages à droite». Cela fonctionne pour certaines intersections, mais il ne donne pas la réponse nécessaire. Ironie du sort, la réponse est dans re, tout simplement pas findall. La réponse ci-dessous re.split()est supérieure.
Jesse Dhillon
4
@JesseDhillon "prendre toutes les sous-chaînes constituées d'une séquence de caractères de mots" et "fractionner sur toutes les sous-chaînes constituées d'une séquence de caractères non verbaux" ne sont littéralement que des façons différentes d'exprimer la même opération; Je ne sais pas pourquoi vous appelleriez l'une ou l'autre réponse supérieure.
Mark Amery
4
@TMWP: L'apostophe signifie qu'un mot comme don'test traité comme un seul mot, plutôt que d'être divisé en donet t.
RichieHindle
574

re.split ()

re.split (motif, chaîne [, maxsplit = 0])

Fractionner la chaîne par les occurrences du motif. Si des parenthèses de capture sont utilisées dans le modèle, le texte de tous les groupes du modèle est également renvoyé dans la liste résultante. Si maxsplit est différent de zéro, au plus maxsplit se produit et le reste de la chaîne est renvoyé en tant qu'élément final de la liste. (Remarque d'incompatibilité: dans la version originale de Python 1.5, maxsplit a été ignoré. Cela a été corrigé dans les versions ultérieures.)

>>> 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
la source
13
Cette solution a l'avantage d'être facilement adaptable pour se diviser également sur des traits de soulignement, ce que la solution findall ne fait pas: print re.split ("\ W + | _", "Testing this_thing") 'renvoie: [' Testing ',' this ' , 'chose']
Emil Stenström
63
Maintenant , si seulement je pouvais me rappeler la différence entre \w, \W, \set \S. Celui qui pensait que la capitalisation d'un drapeau devait inverser sa signification doit être abattu par la tête.
ArtOfWarfare
1
Un cas d'utilisation courant du fractionnement de chaîne consiste à supprimer les entrées de chaîne vides du résultat final. Est-il possible de le faire avec cette méthode? re.split ('\ W +', 'abc') donne ['', 'a', 'b', 'c', '']
Scott Morken
3
@ArtOfWarfare Il est courant d'utiliser la shiftclé pour faire le contraire de quelque chose. ctrl+zdéfaire contre ctrl+shift+zpour refaire. Alors shift w, ou W, serait le contraire de w.
Frank Vel
1
Cette réponse doit être en haut - c'est la seule qui répond précisément au titre de la question.
Kranach
381

Un autre moyen rapide de le faire sans expression rationnelle consiste à remplacer d'abord les caractères, comme ci-dessous:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']
Louis LC
la source
71
Rapide et sale mais parfait pour mon cas (mes séparateurs étaient un petit ensemble connu)
Andy Baker
7
Parfait pour le cas où vous n'avez pas accès à la bibliothèque RE, comme certains petits microcontrôleurs. :-)
tu-Reinstate Monica-dor duh
11
Je pense que c'est aussi plus explicite que RE, donc c'est plutôt gentil avec noob. Parfois, il n'est pas nécessaire de trouver une solution générale à tout
Adam Hughes
Impressionnant. J'avais un .split () dans une situation à entrées multiples et je devais intercepter lorsque l'utilisateur, moi, séparait les entrées par un espace et non par une virgule. J'étais sur le point d'abandonner et de refondre avec re, mais votre solution .replace () a frappé le clou sur la tête. Merci.
JayJay123
cela vous donnera une mauvaise réponse lorsque vous ne voulez pas vous séparer sur des espaces et que vous voulez vous diviser sur d'autres personnages.
Ahmed Amr
307

Tant de réponses, mais je ne trouve aucune solution qui fasse efficacement ce que le titre des questions demande littéralement (fractionnement sur plusieurs séparateurs possibles - à la place, de nombreuses réponses se divisent sur tout ce qui n'est pas un mot, ce qui est différent). Voici donc une réponse à la question du titre, qui s'appuie sur le remodule standard et efficace 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']

où:

  • les […]matchs l' un des séparateurs, listés dans
  • le \-dans l'expression régulière est ici pour empêcher l'interprétation spéciale de -comme indicateur de plage de caractères (comme dans A-Z),
  • le +saute un ou plusieurs délimiteurs (il pourrait être omis grâce à la filter(), mais cela produirait inutilement des chaînes vides entre les séparateurs appariés), et
  • filter(None, …) supprime les chaînes vides éventuellement créées par les séparateurs de début et de fin (car les chaînes vides ont une fausse valeur booléenne).

Cela re.split()"se divise précisément avec plusieurs séparateurs", comme demandé dans le titre de la question.

Cette solution est en outre à l'abri des problèmes de caractères non ASCII dans les mots trouvés dans certaines autres solutions (voir le premier commentaire à la réponse de ghostdog74 ).

Le remodule est beaucoup plus efficace (en vitesse et en concision) que de faire des boucles et des tests Python "à la main"!

Eric O Lebigot
la source
3
"Je ne trouve aucune solution qui fasse efficacement ce que le titre des questions pose littéralement" - la deuxième réponse fait cela, publiée il y a 5 ans: stackoverflow.com/a/1059601/2642204 .
BartoszKP du
17
Cette réponse ne se divise pas au niveau des délimiteurs (à partir d'un ensemble de plusieurs délimiteurs): elle se divise à la place en tout ce qui n'est pas alphanumérique. Cela dit, je conviens que l'intention de l'affiche originale est probablement de ne conserver que les mots, au lieu de supprimer certains signes de ponctuation.
Eric O Lebigot
EOL: Je pense que cette réponse se divise sur un ensemble de plusieurs délimètres. Si vous ajoutez des caractères non alphanumériques à la chaîne qui ne sont pas spécifiés, comme le trait de soulignement, ils ne sont pas fractionnés, comme prévu.
GravityWell
@GravityWell: Je ne suis pas sûr de comprendre: pouvez-vous donner un exemple concret?
Eric O Lebigot
3
@EOL: Je viens de réaliser que j'étais confus par votre commentaire "Cette réponse ne se divise pas ..." Je pensais que "ceci" faisait référence à votre réponse re.split, mais je réalise maintenant que vous vouliez dire la réponse de Gimel. Je pense que CETTE réponse (la réponse à laquelle je commente) est la meilleure réponse :)
GravityWell
56

Une autre façon, sans 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()
ghostdog74
la source
8
Cette solution est en fait meilleure que celle acceptée. Cela fonctionne sans caractères ASCII, essayez "Hey, you - what are you doing here María!?". La solution acceptée ne fonctionnera pas avec l'exemple précédent.
Christopher Ramírez
4
Je pense qu'il y a un petit problème ici ... Votre code ajoutera des caractères séparés par des signes de ponctuation et ne les séparera donc pas ... Si je ne me trompe pas, votre dernière ligne devrait être:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu
La bibliothèque d'expressions régulières peut être conçue pour accepter les conventions Unicode pour les caractères si nécessaire. De plus, cela a le même problème que la solution acceptée auparavant: comme c'est le cas maintenant, il se divise en apostrophes. Vous pouvez le vouloir o for o in s if (o in not string.punctuation or o == "'"), mais alors cela devient trop compliqué pour un one-liner si nous ajoutons également le patch de cedbeu.
Daniel H
Il y a un autre problème ici. Même lorsque nous prenons en compte les changements de @cedbeu, ce code ne fonctionne pas si la chaîne ressemble à quelque chose "First Name,Last Name,Street Address,City,State,Zip Code"et que nous voulons diviser uniquement sur une virgule ,. La sortie souhaitée serait: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Ce que nous obtenons à la place:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker
4
Cette solution est terriblement inefficace: d'abord la liste est déconstruite en caractères individuels, puis l' ensemble des caractères de ponctuation est parcouru pour chaque caractère de la chaîne d'origine, puis les caractères sont assemblés, puis divisés à nouveau. Tout ce "mouvement" est également très compliqué par rapport à une solution basée sur une expression régulière: même si la vitesse n'a pas d'importance dans une application donnée, il n'est pas nécessaire d'avoir une solution compliquée. Étant donné que le remodule est standard et donne à la fois lisibilité et vitesse, je ne vois pas pourquoi il devrait être évité.
Eric O Lebigot
39

Pro-Tip: Utilisez string.translatepour les opérations de chaîne les plus rapides que Python ait.

Une preuve ...

Tout d'abord, la manière lente (désolé 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

Ensuite, nous utilisons re.findall()(comme indiqué par la réponse suggérée). Plus vite:

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

Enfin, nous utilisons 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

Explication:

string.translateest implémenté en C et contrairement à de nombreuses fonctions de manipulation de chaînes en Python, string.translate ne produit nouvelle chaîne. C'est donc aussi rapide que possible pour la substitution de chaînes.

C'est un peu gênant, cependant, car il a besoin d'une table de traduction pour faire cette magie. Vous pouvez créer une table de traduction avec la maketrans()fonction confort. L'objectif ici est de traduire tous les caractères indésirables dans des espaces. Un substitut un pour un. Encore une fois, aucune nouvelle donnée n'est produite. C'est donc rapide !

Ensuite, nous utilisons du bon vieux split(). split()par défaut, fonctionnera sur tous les caractères d'espacement, en les regroupant pour la division. Le résultat sera la liste des mots que vous souhaitez. Et cette approche est presque 4 fois plus rapide que re.findall()!

Dave
la source
4
J'ai fait un test ici, et si vous devez utiliser unicode, l'utilisation patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)est plus rapide que translate, car vous devez coder la chaîne avant d'appliquer la transformation et décoder chaque élément de la liste après la division pour revenir à l'unicode.
Rafael S. Calsaverini
Vous pouvez ajouter une ligne à l'implémentation de la traduction et vous assurer que S ne figure pas parmi les séparateurs avec:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
tables de cuisson du
Aucune prise. Vous comparez des pommes et des oranges. ;) ma solution en python 3 fonctionne toujours; P et prend en charge les séparateurs multi-caractères. :) essayez de le faire de manière simple sans allouer de nouvelle chaîne. :) mais vrai, le mien se limite à l'analyse des paramètres de ligne de commande et non à un livre par exemple.
pprzemek
vous dites "ne produit pas de nouvelle chaîne", ce qui signifie qu'il fonctionne en place sur une chaîne donnée? Je l'ai testé maintenant avec python 2.7 et il ne modifie pas la chaîne oroginale et en renvoie une nouvelle.
Prokop Hapala
26

J'avais un dilemme similaire et je ne voulais pas utiliser le module «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
la source
1
J'aime ça. Juste une note, l'ordre des séparateurs est important. Désolé si c'est évident.
crizCraig
2
Pourquoi ne pas utiliser le remodule, qui est à la fois plus rapide et plus clair (pas que les expressions régulières soient particulièrement claires, mais parce qu'il est beaucoup plus court et direct)?
Eric O Lebigot
13

Tout d'abord, je veux convenir avec d'autres que les regex ou les str.translate(...)solutions basées sont les plus performantes. Pour mon cas d'utilisation, les performances de cette fonction n'étaient pas significatives, j'ai donc voulu ajouter des idées que j'ai considérées avec ces critères.

Mon objectif principal était de généraliser les idées de certaines des autres réponses en une solution qui pourrait fonctionner pour des chaînes contenant plus que des mots regex (c.-à-d., Mettre sur liste noire le sous-ensemble explicite de caractères de ponctuation par rapport aux caractères de mots sur liste blanche).

Notez que, dans toute approche, on pourrait également envisager d'utiliser string.punctuationà la place d'une liste définie manuellement.

Option 1 - re.sub

J'ai été surpris de ne pas avoir trouvé de réponse jusqu'à présent sur re.sub (...) . Je trouve que c'est une approche simple et naturelle de ce problème.

import re

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

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

Dans cette solution, j'ai imbriqué l'appel à l' re.sub(...)intérieur re.split(...)- mais si les performances sont essentielles, la compilation de l'expression régulière à l'extérieur pourrait être bénéfique - pour mon cas d'utilisation, la différence n'était pas significative, donc je préfère la simplicité et la lisibilité.

Option 2 - remplacement str.

Il s'agit de quelques lignes supplémentaires, mais cela a l'avantage d'être extensible sans avoir à vérifier si vous devez échapper à un certain personnage dans regex.

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

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

words = my_str.split()

Cela aurait été bien de pouvoir mapper le str.replace à la chaîne à la place, mais je ne pense pas que cela puisse être fait avec des chaînes immuables, et tout en mappant avec une liste de caractères fonctionnerait, exécuter chaque remplacement contre chaque caractère semble excessif. (Modifier: Voir l'option suivante pour un exemple fonctionnel.)

Option 3 - functools.reduce

(En Python 2, reduceest disponible dans l'espace de noms global sans l'importer depuis 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
la source
Hm, une autre méthode est à utiliser str.translate- elle n'est pas compatible Unicode mais est probablement plus rapide que d'autres méthodes et en tant que telle peut être bonne dans certains cas: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))ici aussi, il est obligatoire d'avoir des remplacements comme une chaîne de caractères, pas de tuple ou liste.
MarSoft
@MarSoft Merci! J'ai mentionné celui-là en haut de la réponse mais j'ai décidé de ne pas l'ajouter car les réponses existantes en parlaient déjà 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]

Ensuite, cela devient un trois lignes:

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

Explication

C'est ce que l'on appelle à Haskell la monade de la Liste. L'idée derrière la monade est qu'une fois "dans la monade" vous "restez dans la monade" jusqu'à ce que quelque chose vous enlève. Par exemple, dans Haskell, disons que vous mappez la range(n) -> [1,2,...,n]fonction python sur une liste. Si le résultat est une liste, il sera ajouté à la liste en place, vous obtiendrez donc quelque chose comme map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Ceci est connu comme map-append (ou mappend, ou peut-être quelque chose comme ça). L'idée ici est que vous avez cette opération que vous appliquez (fractionnement sur un jeton), et chaque fois que vous faites cela, vous joignez le résultat dans la liste.

Vous pouvez résumer cela dans une fonction et l'avoir tokens=string.punctuationpar défaut.

Avantages de cette approche:

  • Cette approche (contrairement aux approches naïves basées sur les regex) peut fonctionner avec des jetons de longueur arbitraire (ce que les regex peuvent également faire avec une syntaxe plus avancée).
  • Vous n'êtes pas limité à de simples jetons; vous pourriez avoir une logique arbitraire à la place de chaque jeton, par exemple l'un des "jetons" pourrait être une fonction qui se divise selon la façon dont les parenthèses sont imbriquées.
ninjagecko
la source
Solution Haskell soignée, mais IMO cela peut être écrit plus clairement sans mappend en Python.
Vlad l'Impala
@Goose: le fait était que la fonction à 2 lignes map_then_appendpeut être utilisée pour rendre un problème à 2 lignes , ainsi que de nombreux autres problèmes beaucoup plus faciles à écrire. La plupart des autres solutions utilisent le remodule d' expression régulière , qui n'est pas python. Mais je suis mécontent de la façon dont je donne à ma réponse une apparence inélégante et gonflée quand elle est vraiment concise ... Je vais la modifier ...
ninjagecko
est-ce censé fonctionner en Python tel qu'il est écrit? mon fragmentsrésultat est juste une liste des caractères de la chaîne (y compris les jetons).
Rick soutient Monica
@RickTeachey: cela fonctionne pour moi à la fois en python2 et en python3.
ninjagecko
hmmmm. Peut-être que l'exemple est un peu ambigu. J'ai essayé le code dans la réponse de toutes sortes de différentes façons - y compris d' avoir fragments = ['the,string'], fragments = 'the,string'ou fragments = list('the,string')et aucun d'entre eux produisent la sortie droite.
Rick soutient Monica
5

essaye ça:

import re

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

cela imprimera ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Corey Goldberg
la source
4

Utilisez remplacer deux fois:

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

résulte en:

['11223', '33344', '33222', '3344']
jeroen
la source
4

J'aime re , mais voici ma solution sans elle:

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 .__ contains__ est une méthode utilisée par l'opérateur 'in'. Fondamentalement, c'est la même chose que

lambda ch: ch in sep

mais c'est plus pratique ici.

groupby obtient notre chaîne et notre fonction. Il divise la chaîne en groupes en utilisant cette fonction: chaque fois qu'une valeur de fonction change - un nouveau groupe est généré. Donc, sep .__ contient__ est exactement ce dont nous avons besoin.

groupby renvoie une séquence de paires, où la paire [0] est le résultat de notre fonction et la paire [1] est un groupe. En utilisant «sinon k», nous filtrons les groupes avec des séparateurs (car un résultat de sep .__ contient__ est vrai sur les séparateurs). Eh bien, c'est tout - nous avons maintenant une séquence de groupes où chacun est un mot (le groupe est en fait un itérable, nous utilisons donc join pour le convertir en chaîne).

Cette solution est assez générale, car elle utilise une fonction pour séparer la chaîne (vous pouvez diviser par n'importe quelle condition dont vous avez besoin). En outre, il ne crée pas de chaînes / listes intermédiaires (vous pouvez supprimer la jointure et l'expression deviendra paresseuse, car chaque groupe est un itérateur)

monitorius
la source
4

Au lieu d'utiliser une fonction de module re re.split, vous pouvez obtenir le même résultat en utilisant la méthode series.str.split des pandas.

Créez d'abord une série avec la chaîne ci-dessus, puis appliquez la méthode à la série.

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

Le paramètre pat prend les délimiteurs et renvoie la chaîne divisée sous forme de tableau. Ici, les deux délimiteurs sont passés en utilisant un | (ou opérateur). La sortie est la suivante:

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

Tarun Kumar Yellapu
la source
1
Ce n'est pas une question de verbose mais, plutôt le fait d'importer une bibliothèque entière (que j'adore, BTW) pour effectuer une tâche simple après avoir converti une chaîne en une série panda. Pas très "sympa pour l'occam".
zar3bski
3

Je me familiarise à nouveau avec Python et j'avais besoin de la même chose. La solution findall peut être meilleure, mais j'ai trouvé ceci:

tokens = [x.strip() for x in data.split(',')]
Leon Starr
la source
Intelligent, devrait fonctionner sur toutes les constructions grammaticales anglaises auxquelles je peux penser, sauf un em-dash sans espaces - ceci, par exemple. (Solution de contournement.)
ninjagecko
3

en utilisant maketrans et traduire, vous pouvez le faire facilement et proprement

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
Ritesh Sinha
la source
Grande réponse comme pour Python> = 3,6
revliscano
3

En Python 3, vous pouvez utiliser la méthode de PY4E - Python for Everybody .

Nous pouvons résoudre ces deux problèmes en utilisant les méthodes de chaînes lower, punctuationet translate. C'est translatela méthode la plus subtile. Voici la documentation pour translate:

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

Remplacez les caractères par fromstrpar le caractère à la même position dans tostret supprimez tous les caractères qui se trouvent deletestr. Les fromstret tostrpeuvent être des chaînes vides et le deletestrparamètre peut être omis.

Vous pouvez voir la "ponctuation":

In [10]: import string

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

Pour votre exemple:

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

Pour plus d'informations, vous pouvez vous référer:

Jeremy Anifacc
la source
2
Les méthodes translate () et maketrans () des chaînes sont intéressantes, mais cette méthode ne parvient pas à "se séparer au niveau des délimiteurs" (ou des espaces): par exemple, "il y avait un grand effondrement" produira à tort le mot "cavein" à la place de la «grotte» et du «in» attendus… Ainsi, cela ne fait pas ce que la question demande.
Eric O Lebigot
Tout comme ce que @EricLebigot a commenté. La méthode ci-dessus ne fait pas très bien ce que la question demande.
Jeremy Anifacc
2

Une autre façon d'y parvenir consiste à utiliser le Kit d'outils en langage naturel ( nltk ).

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

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

Le plus gros inconvénient de cette méthode est que vous devez installer le package nltk .

Les avantages sont que vous pouvez faire beaucoup de choses amusantes avec le reste du paquet nltk une fois que vous avez obtenu vos jetons.

tgray
la source
1

Tout d'abord, je ne pense pas que votre intention soit réellement d'utiliser la ponctuation comme délimiteurs dans les fonctions de division. Votre description suggère que vous souhaitez simplement éliminer la ponctuation des chaînes résultantes.

Je rencontre cela assez fréquemment, et ma solution habituelle ne nécessite pas de re.

Fonction lambda à une ligne avec compréhension de la liste:

(nécessite 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']


Fonction (traditionnelle)

En tant que fonction traditionnelle, il ne s'agit que de deux lignes avec une compréhension de liste (en plus 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']

Il laissera également naturellement les contractions et les mots avec trait d'union intacts. Vous pouvez toujours utiliser text.replace("-", " ")pour transformer les tirets en espaces avant la séparation.

Fonction générale sans Lambda ou compréhension de liste

Pour une solution plus générale (où vous pouvez spécifier les caractères à éliminer), et sans compréhension de liste, vous obtenez:

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

Bien sûr, vous pouvez toujours généraliser la fonction lambda à n'importe quelle chaîne de caractères spécifiée.

cosmicFluke
la source
1

Tout d'abord, utilisez toujours re.compile () avant d'effectuer une opération RegEx en boucle car cela fonctionne plus rapidement que le fonctionnement normal.

Donc, pour votre problème, compilez d'abord le modèle, puis effectuez une action dessus.

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

Voici la réponse avec quelques explications.

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'

ou en une seule ligne, on peut faire comme ça:

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

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

réponse mise à jour

Tasneem Haider
la source
1

Créez une fonction qui prend en entrée deux chaînes (la chaîne source à diviser et la chaîne de liste de séparation des délimiteurs) et génère une liste de mots divisés:

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
la source
1

J'aime la solution de pprzemek car elle ne suppose pas que les délimiteurs sont des caractères uniques et n'essaie pas de tirer parti d'une regex (ce qui ne fonctionnerait pas bien si le nombre de séparateurs devenait fou).

Voici une version plus lisible de la solution ci-dessus pour plus de clarté:

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
la source
0

eu le même problème que @ooboo et trouver ce sujet @ ghostdog74 m'a inspiré, peut-être que quelqu'un trouve ma solution utile

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

entrez quelque chose à la place de l'espace et divisez en utilisant le même caractère si vous ne voulez pas diviser les espaces.

badas
la source
Et si je dois diviser en utilisant Word?
Harsha Biyani
0

Voici mon coup à une scission avec plusieurs suppresseurs:

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
la source
0

Je pense que ce qui suit est la meilleure réponse pour répondre à vos besoins:

\W+ peut convenir à ce cas, mais peut ne pas convenir à d'autres cas.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")
nemozhp
la source
Je suis d'accord, les solutions \wet \Wne sont pas une réponse à (le titre de) la question. Notez que dans votre réponse, |devrait être supprimé (vous pensez à la expr0|expr1place de [char0 char1…]). De plus, compile()l'expression régulière n'est pas nécessaire .
Eric O Lebigot
0

Voici ma façon de voir les choses ....

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
la source
0

J'aime replace()la meilleure façon. La procédure suivante remplace tous les séparateurs définis dans une chaîne splitlistpar le premier séparateur dans splitlist, puis divise le texte sur ce séparateur. Il prend également en compte si splitlistse trouve être une chaîne vide. Il renvoie une liste de mots, sans aucune chaîne vide.

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

Voici l'utilisation:

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

Si vous souhaitez une opération réversible (conserver les délimiteurs), vous pouvez utiliser cette fonction:

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
la source
0

J'ai récemment eu besoin de le faire, mais je voulais une fonction qui correspondait quelque peu à la str.splitfonction de bibliothèque standard , cette fonction se comporte de la même manière que la bibliothèque standard lorsqu'elle est appelée avec 0 ou 1 arguments.

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

REMARQUE : Cette fonction n'est utile que lorsque vos séparateurs sont constitués d'un seul caractère (comme c'était mon cas).

Justin Fay
la source