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?
str.split()
fonctionne également sans aucun argumentRéponses:
Un cas où les expressions régulières sont justifiées:
la source
re
, tout simplement pasfindall
. La réponse ci-dessousre.split()
est supérieure.don't
est traité comme un seul mot, plutôt que d'être divisé endon
ett
.re.split ()
la source
\w
,\W
,\s
et\S
. Celui qui pensait que la capitalisation d'un drapeau devait inverser sa signification doit être abattu par la tête.shift
clé pour faire le contraire de quelque chose.ctrl+z
défaire contrectrl+shift+z
pour refaire. Alorsshift w
, ouW
, serait le contraire dew
.Un autre moyen rapide de le faire sans expression rationnelle consiste à remplacer d'abord les caractères, comme ci-dessous:
la source
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
re
module standard et efficace de Python :où:
[…]
matchs l' un des séparateurs, listés dans\-
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 dansA-Z
),+
saute un ou plusieurs délimiteurs (il pourrait être omis grâce à lafilter()
, mais cela produirait inutilement des chaînes vides entre les séparateurs appariés), etfilter(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
re
module est beaucoup plus efficace (en vitesse et en concision) que de faire des boucles et des tests Python "à la main"!la source
Une autre façon, sans regex
la source
"Hey, you - what are you doing here María!?"
. La solution acceptée ne fonctionnera pas avec l'exemple précédent.''.join([o if not o in string.punctuation else ' ' for o in s]).split()
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."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']
re
module est standard et donne à la fois lisibilité et vitesse, je ne vois pas pourquoi il devrait être évité.Pro-Tip: Utilisez
string.translate
pour les opérations de chaîne les plus rapides que Python ait.Une preuve ...
Tout d'abord, la manière lente (désolé pprzemek):
Ensuite, nous utilisons
re.findall()
(comme indiqué par la réponse suggérée). Plus vite:Enfin, nous utilisons
translate
:Explication:
string.translate
est 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 quere.findall()
!la source
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.s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
J'avais un dilemme similaire et je ne voulais pas utiliser le module «re».
la source
re
module, 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)?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.
Dans cette solution, j'ai imbriqué l'appel à l'
re.sub(...)
intérieurre.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.
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,
reduce
est disponible dans l'espace de noms global sans l'importer depuis functools.)la source
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.Ensuite, cela devient un trois lignes:
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 commemap(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.punctuation
par défaut.Avantages de cette approche:
la source
map_then_append
peut ê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 lere
module 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 ...fragments
résultat est juste une liste des caractères de la chaîne (y compris les jetons).fragments = ['the,string']
,fragments = 'the,string'
oufragments = list('the,string')
et aucun d'entre eux produisent la sortie droite.essaye ça:
cela imprimera
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
la source
Utilisez remplacer deux fois:
résulte en:
la source
J'aime re , mais voici ma solution sans elle:
sep .__ contains__ est une méthode utilisée par l'opérateur 'in'. Fondamentalement, c'est la même chose que
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)
la source
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!?]
la source
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:
la source
en utilisant maketrans et traduire, vous pouvez le faire facilement et proprement
la source
En Python 3, vous pouvez utiliser la méthode de PY4E - Python for Everybody .
your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))
Vous pouvez voir la "ponctuation":
Pour votre exemple:
Pour plus d'informations, vous pouvez vous référer:
la source
Une autre façon d'y parvenir consiste à utiliser le Kit d'outils en langage naturel ( nltk ).
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.
la source
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
):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
):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:
Bien sûr, vous pouvez toujours généraliser la fonction lambda à n'importe quelle chaîne de caractères spécifiée.
la source
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.
la source
Voici la réponse avec quelques explications.
ou en une seule ligne, on peut faire comme ça:
réponse mise à jour
la source
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:
la source
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é:
la source
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
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.
la source
Voici mon coup à une scission avec plusieurs suppresseurs:
la source
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.la source
\w
et\W
ne sont pas une réponse à (le titre de) la question. Notez que dans votre réponse,|
devrait être supprimé (vous pensez à laexpr0|expr1
place de[char0 char1…]
). De plus,compile()
l'expression régulière n'est pas nécessaire .Voici ma façon de voir les choses ....
la source
J'aime
replace()
la meilleure façon. La procédure suivante remplace tous les séparateurs définis dans une chaînesplitlist
par le premier séparateur danssplitlist
, puis divise le texte sur ce séparateur. Il prend également en compte sisplitlist
se trouve être une chaîne vide. Il renvoie une liste de mots, sans aucune chaîne vide.la source
Voici l'utilisation:
la source
Si vous souhaitez une opération réversible (conserver les délimiteurs), vous pouvez utiliser cette fonction:
la source
J'ai récemment eu besoin de le faire, mais je voulais une fonction qui correspondait quelque peu à la
str.split
fonction 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.REMARQUE : Cette fonction n'est utile que lorsque vos séparateurs sont constitués d'un seul caractère (comme c'était mon cas).
la source