Séparer les chaînes avec plusieurs délimiteurs?

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

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

devrait être

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

mais str.split() de Python ne fonctionne qu'avec un seul argument... Donc j'ai tous les mots avec la ponctuation après que je me suis séparé avec whitespace. Des idées?

520
demandé sur martineau 2009-06-29 21:49:35

29 réponses

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']
380
répondu RichieHindle 2017-05-23 10:50:29

re.split ()

re.split (pattern, string[, maxsplit=0])

Split chaîne par les occurrences du motif. Si les parenthèses de capture sont utilisées dans le modèle, alors le texte de tous les groupes dans le modèle sont également retournés dans la liste résultante. Si maxsplit est nonzero, au plus maxsplit les séparations se produisent, et le reste de la chaîne est retourné comme l'élément final de la liste. (Incompatibilité note: dans la version originale de Python 1.5, maxsplit a été ignoré. Ceci a été corrigé dans des 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.']
440
répondu gimel 2009-06-29 17:57:49

un autre moyen rapide de faire cela sans un regexp est de remplacer les caractères en premier, comme ci-dessous:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']
278
répondu Louis LC 2011-08-27 16:10:52

tant de réponses, mais je ne peux trouver aucune solution qui fait efficacement ce que le titre des questions demande littéralement (fractionnement sur plusieurs séparateurs possibles-à la place, de nombreuses réponses supprimer tout ce qui n'est pas un mot, qui est différent). Voici donc une réponse à la question du titre, qui s'appuie sur le module standard et efficace re 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ù:

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

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

cette solution ne souffre pas non plus de problèmes avec les caractères non-ASCII dans les mots, aussi bien (voir le premier commentaire à réponse de ghostdog74 ).

le module re est beaucoup plus efficace que de faire des boucles Python et des tests "à la main".

217
répondu Eric Lebigot 2018-05-18 13:29:07

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()
47
répondu ghostdog74 2013-05-18 02:40:35

Pro-Tip: utilisez string.translate pour les opérations de chaîne les plus rapides de Python.

une preuve...

D'abord, la voie 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() (selon la réponse suggérée). Beaucoup plus rapide:

>>> 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.translate est implémenté en C et contrairement à de nombreuses fonctions de manipulation de chaîne en Python, string.translate ne produit pas une nouvelle chaîne. Donc c'est à peu près aussi rapide que vous pouvez obtenir pour la substitution de chaîne.

c'est un peu embarrassant, cependant, comme il a besoin d'une table de traduction afin de faire cette magie. Vous pouvez faire une table de traduction avec la fonction de commodité maketrans() . L'objectif ici est de traduire tous les caractères indésirables espace. Un un-pour-un substitut. Encore une fois, aucune nouvelle donnée n'est produite. Donc c'est fast !

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

35
répondu Dave 2016-03-01 06:27:16

réponse un peu tardive :), mais 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']
20
répondu pprzemek 2010-05-26 11:04:20
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]

puis cela devient un triple-liner:

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

explication

C'est ce qui dans Haskell est connu sous le nom de la liste monad. L'idée derrière la monade est qu'une fois "dans la monade" vous "rester dans la monade" jusqu'à ce que quelque chose vous prend. Par exemple dans Haskell, dites que vous mappez la fonction python range(n) -> [1,2,...,n] sur une liste. Si le résultat est une Liste, il sera ajouter à la Liste en place, pour avoir quelque chose comme map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0] . Ceci est connu sous le nom de map-append (ou mappend, ou peut-être quelque chose comme ça). L'idée ici est que vous avez cette opération vous postulez (fractionnement sur un jeton), et à chaque fois que vous faites cela, vous rejoignez le résultat dans la liste.

vous pouvez l'abstraire dans une fonction et avoir tokens=string.punctuation par défaut.

avantages de cette approche:

  • cette approche (contrairement à les approches basées sur regex naïves) peuvent fonctionner avec des tokens de longueur arbitraire (ce que regex peut aussi faire avec une syntaxe plus avancée).
  • vous n'êtes pas limité à de simples tokens; vous pourriez avoir une logique arbitraire à la place de chaque token, par exemple un des "tokens" pourrait être une fonction qui se divise en fonction de la façon dont les parenthèses imbriquées sont.
10
répondu ninjagecko 2012-08-30 03:44:31

tout d'abord, je veux être d'accord avec les autres pour dire que les solutions basées sur regex ou str.translate(...) sont les plus performantes. Pour mon cas d'utilisation la performance de cette fonction n'était pas significative, donc je voulais ajouter des idées que j'ai considérées avec ce critère.

mon but principal était de généraliser des idées à partir de certaines des autres réponses dans une solution qui pourrait fonctionner pour les chaînes contenant plus que de simples mots regex (i.e., la liste noire le sous-ensemble explicite de caractères de ponctuation vs une liste blanche des caractères de mot).

noter que, dans n'importe quelle approche, on pourrait également envisager d'utiliser string.punctuation au lieu d'une liste définie manuellement.

Option 1-re.sub

j'ai été surpris de voir pas de réponse jusqu'à présent utilise re.sous.(..) . 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 imité l'appel à re.sub(...) inside re.split(...) - mais si la performance est critique, compiler le regex à 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-str.remplacer

il s'agit de quelques lignes supplémentaires, mais il a l'avantage d'être extensible sans avoir à vérifier si vous devez échapper à un certain caractère 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()

il aurait ce fut agréable de pouvoir cartographier la str.remplacer à la chaîne de caractères à la place, mais je ne pense pas que cela puisse être fait avec des chaînes immuables, et alors que la mise en correspondance 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.réduire les

(en Python 2, reduce est disponible dans l'Espace-nom global sans l'importer des 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()
8
répondu Taylor Edmiston 2017-09-14 15:58:47

essayez ceci:

import re

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

ce sera imprimé ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

4
répondu Corey Goldberg 2009-06-29 18:01:00

remplacer deux fois:

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

résultats dans:

['11223', '33344', '33222', '3344']
4
répondu jeroen 2012-03-30 14:17:54

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.__contient__ est une méthode utilisée par les 'dans' l'opérateur. Fondamentalement, il est le même que

lambda ch: ch in sep

mais est plus pratique ici.

groupby reçoit notre chaîne et fonction. Il divise la chaîne dans les groupes en utilisant cette fonction: chaque fois qu'une valeur de la fonction change - un nouveau groupe est généré. So, sep.__contient__ est exactement ce dont nous avons besoin.

groupe renvoie une séquence de paires paire[0] est le résultat de notre fonction et notre paire[1] est un groupe. En utilisant 'if not k ' nous filtrons les groupes avec des séparateurs (parce qu'un résultat de sep.__contient__ est Vrai que sur les séparateurs). Eh bien, c'est tout - maintenant, nous avons une séquence de groupes où chacun est un mot (groupe en fait un itérable donc nous utilisons join pour le convertir en chaîne de caractères).

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 rejoindre et l'expression deviendra paresseuse, puisque chaque groupe est un itérateur)

4
répondu monitorius 2013-10-06 17:30:05

je me retrouve avec Python et j'avais besoin de la même chose. La solution de findall est peut-être meilleure, mais j'ai trouvé ceci:

tokens = [x.strip() for x in data.split(',')]
3
répondu Leon Starr 2012-04-20 16:56:13

au lieu d'utiliser une fonction re module re.split vous pouvez obtenir le même résultat en utilisant la série.Str.méthode de fractionnement des pandas.

tout d'abord, créer une série avec la chaîne ci-dessus et ensuite appliquer la méthode à la série.

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

paramètre pat prend les délimiteurs et renvoie la chaîne de caractères fragmentée comme un tableau. Ici, les deux délimiteurs sont passés en utilisant un / (ou un opérateur). La sortie est comme suit:

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

2
répondu Tarun Kumar Yellapu 2018-09-10 15:32:40

une autre façon d'y parvenir est d'utiliser la boîte à outils du 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

Cette affiche: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

le plus grand inconvénient de cette méthode est que vous devez installer le paquet nltk .

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

1
répondu tgray 2009-06-29 18:51:37

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

je rencontre ceci assez fréquemment, et ma solution habituelle ne nécessite pas de Ré.

One-liner fonction lambda w/ compréhension de 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, ce n'est encore que 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']

elle laissera naturellement intactes les contractions et les mots avec un trait d'Union. Vous pouvez toujours utiliser text.replace("-", " ") pour transformer les traits d'Union en espaces avant la séparation.

fonction générale sans Lambda ou List Compréhension

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 quel chaîne de caractères.

1
répondu cosmicFluke 2014-11-04 20:02:46

tout d'abord, toujours utiliser re.compiler () avant d'effectuer toute opération RegEx dans une boucle parce qu'elle fonctionne plus rapidement que l'opération normale.

donc, pour votre problème, compilez d'abord le motif puis effectuez l'action sur celui-ci.

import re
DATA = "Hey, you - what are you doing here!?"
reg_tok = re.compile("[\w']+")
print reg_tok.findall(DATA)
1
répondu shrikant 2015-11-23 15:12:17

, 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 ligne, nous pouvons faire comme ceci:

(''.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

1
répondu Tasneem Haider 2016-06-06 10:59:23

crée une fonction qui prend en entrée deux chaînes (la chaîne source à diviser et la chaîne splitlist des délimiteurs) et affiche une liste de mots séparé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
1
répondu user852006 2018-02-14 18:37:05

en utilisant maketrans et translate vous pouvez le faire facilement et proprement

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
1
répondu Ritesh Sinha 2018-03-03 23:59:23

a 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 dans l'espace place et séparez en utilisant le même caractère si vous ne voulez pas diviser aux espaces.

0
répondu badas 2011-03-15 10:12:20

voici mon go à une scission avec des délimiteurs multiples:

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
0
répondu Martlark 2011-08-06 11:38:15

je pense que ce qui suit est la meilleure réponse à vos besoins suite :

\W+ peut-être approprié pour cette affaire, mais peut ne pas être approprié pour d'autres cas.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")
0
répondu nemozhp 2012-03-10 08:30:04

C'est mon avis....

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']
0
répondu Arindam Roychowdhury 2013-04-29 05:32:04

j'aime replace() façon la meilleure. La procédure suivante change tous les séparateurs définis dans une chaîne splitlist au premier séparateur dans splitlist et divise ensuite le texte sur ce séparateur. Il représente également si splitlist se trouve être une chaîne vide. Il retourne une liste de mots, sans chaînes vides.

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]
0
répondu Stefan van den Akker 2014-02-07 23:15:39
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'usage:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
0
répondu inspectorrr 2014-02-20 13:19:43

si vous voulez une opération réversible (préserver 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
-1
répondu Nadav B 2018-01-22 08:25:18

en Python 3, vous pouvez utiliser la méthode de PY4E - Python pour tout le monde .

nous pouvons résoudre ces deux problèmes en utilisant les méthodes de chaîne lower , punctuation , et translate . Le translate est la plus subtile des méthodes. Voici la documentation pour translate :

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

remplacer les caractères dans fromstr avec le caractère dans la même position dans tostr et supprimer tous les caractères qui sont dans deletestr . Les fromstr et tostr peuvent être des chaînes vides et le paramètre deletestr 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 à:

-1
répondu Jeremy Anifacc 2018-07-15 15:09:06

vous voulez la méthode findall() du module RegEx de Python:

http://www.regular-expressions.info/python.html

exemple

-4
répondu Tyson 2017-05-23 11:55:18