Dividi le stringhe in parole con delimitatori al contorno di più parole


671

Penso che quello che voglio fare sia un compito abbastanza comune ma non ho trovato riferimenti sul web. Ho un testo con punteggiatura e voglio un elenco delle parole.

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

dovrebbe essere

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

Ma di Python str.split() funziona solo con un argomento, quindi ho tutte le parole con la punteggiatura dopo che mi sono diviso con uno spazio bianco. Qualche idea?



6
Python str.split()funziona anche senza argomenti
Ivan Vinogradov,

Risposte:


468

Un caso in cui le espressioni regolari sono giustificate:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

2
Grazie. Tuttavia, sono ancora interessato: come posso implementare l'algoritmo utilizzato in questo modulo? E perché non appare nel modulo stringa?
ooboo,

29
Le espressioni regolari all'inizio possono essere scoraggianti, ma sono molto potenti. L'espressione regolare '\ w +' significa "un carattere di parola (az ecc.) Ripetuto una o più volte". C'è un HOWTO sulle espressioni regolari di Python qui: amk.ca/python/howto/regex
RichieHindle

324
Questa non è la risposta alla domanda. Questa è una risposta a una domanda diversa, che sembra funzionare per questa situazione particolare. È come se qualcuno chiedesse "come faccio a girare a sinistra" e la risposta più votata è stata "prendere le tre svolte a destra successive". Funziona per determinate intersezioni, ma non fornisce la risposta necessaria. Ironia della sorte, la risposta è dentro re, proprio no findall. La risposta che segue re.split()è superiore.
Jesse Dhillon,

4
@JesseDhillon "prendere tutte le sottostringhe costituite da una sequenza di caratteri di parole" e "dividere su tutte le sottostringhe costituite da una sequenza di caratteri di non parole" sono letteralmente solo modi diversi di esprimere la stessa operazione; Non sono sicuro del motivo per cui chiameresti una risposta superiore.
Mark Amery,

4
@TMWP: l'apopopo significa che una parola come don'tviene trattata come una sola parola, anziché essere divisa in done t.
RichieHindle

574

re.split ()

re.split (modello, stringa [, maxsplit = 0])

Dividi la stringa per le occorrenze del modello. Se le parentesi di cattura vengono utilizzate nel modello, anche il testo di tutti i gruppi nel modello viene restituito come parte dell'elenco risultante. Se maxsplit è diverso da zero, si verificano al massimo divisioni maxsplit e il resto della stringa viene restituito come elemento finale dell'elenco. (Nota di incompatibilità: nella versione originale di Python 1.5, maxsplit è stato ignorato. Questo problema è stato risolto nelle versioni successive.)

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

13
Questa soluzione ha il vantaggio di essere facilmente adattabile anche alla suddivisione su caratteri di sottolineatura, cosa che la soluzione findall non ha: print re.split ("\ W + | _", "Testing this_thing") 'produce: [' Testing ',' this ' , "cosa"]
Emil Stenström il

63
Ora, se solo potessi ricordare la differenza tra \w, \W, \s, e \S. Chiunque abbia pensato che la maiuscola di una bandiera dovesse invertire il suo significato deve essere colpito alla testa.
ArtOfWarfare il

1
Un caso d'uso comune di suddivisione delle stringhe è la rimozione di voci di stringa vuote dal risultato finale. È possibile farlo con questo metodo? re.split ('\ W +', 'abc') si traduce in ['', 'a', 'b', 'c', '']
Scott Morken,

3
@ArtOfWarfare È comune usare la shiftchiave per fare il contrario di qualcosa. ctrl+zannulla vs. ctrl+shift+zper ripetere. Quindi shift w, o W, sarebbe l'opposto di w.
Frank Vel,

1
Questa risposta dovrebbe essere al top: è l'unica che risponde esattamente al titolo della domanda.
Kranach,

381

Un altro modo rapido per farlo senza regexp è sostituire prima i caratteri, come di seguito:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']

71
Veloce e sporco ma perfetto per il mio caso (i miei separatori erano un piccolo set noto)
Andy Baker

7
Perfetto per il caso in cui non si ha accesso alla libreria RE, ad esempio alcuni piccoli microcontrollori. :-)
tu-Reinstate Monica-dor duh

11
Penso che questo sia anche più esplicito di RE, quindi è un po 'adatto a nessuno. A volte non è necessaria una soluzione generale per tutto
Adam Hughes,

Eccezionale. Avevo un .split () in una situazione di input multipli, e dovevo catturare quando l'utente, io, separava gli input con uno spazio e non una virgola. Stavo per rinunciare e rifondere con re, ma la tua soluzione .replace () ha colpito l'unghia sulla testa. Grazie.
JayJay123,

ti darà una risposta sbagliata quando non vuoi dividere gli spazi e vuoi dividere gli altri personaggi.
Ahmed Amr,

307

Tante risposte, ma non riesco a trovare alcuna soluzione che faccia effettivamente ciò che il titolo delle domande richiede letteralmente (suddividendo su più possibili separatori, invece molte risposte si dividono su tutto ciò che non è una parola, che è diverso). Quindi, ecco una risposta alla domanda nel titolo, che si basa sul remodulo standard ed efficiente di 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']

dove:

  • le […]partite uno dei separatori elencati all'interno,
  • l' \-espressione regolare è qui per impedire l'interpretazione speciale di -come indicatore di intervallo di caratteri (come in A-Z),
  • i +salti uno o più delimitatori (che possono essere omessi graziefilter() , ma ciò inutilmente produrre stringhe vuote tra separatori abbinati), e
  • filter(None, …) rimuove le stringhe vuote eventualmente create dai separatori iniziali e finali (poiché le stringhe vuote hanno un valore booleano falso).

Questo re.split()precisamente "si divide con più separatori", come richiesto nel titolo della domanda.

Questa soluzione è inoltre immune ai problemi con caratteri non ASCII nelle parole trovate in alcune altre soluzioni (vedi il primo commento alla risposta di ghostdog74 ).

Il remodulo è molto più efficiente (in termini di velocità e concisione) rispetto ai cicli e ai test Python "a mano"!


3
"Non riesco a trovare alcuna soluzione che faccia effettivamente ciò che il titolo delle domande pone letteralmente" - la seconda risposta lo fa, pubblicata 5 anni fa: stackoverflow.com/a/1059601/2642204 .
BartoszKP,

17
Questa risposta non si divide in delimitatori (da un insieme di più delimitatori): si divide invece in tutto ciò che non è alfanumerico. Detto questo, concordo sul fatto che l'intento del poster originale è probabilmente quello di mantenere solo le parole, invece di rimuovere alcuni segni di punteggiatura.
Eric O Lebigot,

EOL: Penso che questa risposta si divida su una serie di più delimitatori. Se si aggiungono alla stringa non alfanumerici non specificati, come il carattere di sottolineatura, questi non vengono suddivisi, come previsto.
GravityWell,

@GravityWell: non sono sicuro di capire: puoi fare un esempio concreto?
Eric O Lebigot l'

3
@EOL: Mi sono appena reso conto di essere confuso dal tuo commento "Questa risposta non si divide ..." Ho pensato "questo" si riferiva alla tua risposta replit, ma ora mi rendo conto che intendevi la risposta di Gimel. Penso che QUESTA risposta (la risposta a cui sto commentando) sia la migliore risposta :)
GravityWell

56

Un altro modo, senza 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()

8
Questa soluzione è in realtà migliore di quella accettata. Funziona senza caratteri ASCII, prova "Hey, you - what are you doing here María!?". La soluzione accettata non funzionerà con l'esempio precedente.
Christopher Ramírez,

4
Penso che ci sia un piccolo problema qui ... Il tuo codice aggiungerà caratteri separati da punteggiatura e quindi non li dividerà ... Se non sbaglio, la tua ultima riga dovrebbe essere:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu

Se necessario, è possibile creare la libreria delle espressioni regolari per accettare le convenzioni Unicode per i caratteri. Inoltre, questo ha lo stesso problema che aveva la soluzione accettata: com'è ora, si divide su apostrofi. Potresti volerlo o for o in s if (o in not string.punctuation or o == "'"), ma poi sta diventando troppo complicato per un one-liner se aggiungiamo anche la patch di cedbeu.
Daniel H,

C'è un altro problema qui. Anche quando prendiamo in considerazione le modifiche di @cedbeu, questo codice non funziona se la stringa è simile "First Name,Last Name,Street Address,City,State,Zip Code"e vogliamo dividere solo su una virgola ,. L'output desiderato sarebbe: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Cosa otteniamo invece:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker,

4
Questa soluzione è terribilmente inefficiente: prima l'elenco viene decostruito in singoli caratteri, quindi l' intero set di caratteri di punteggiatura viene esaminato per ogni singolo carattere nella stringa originale, quindi i caratteri vengono riuniti nuovamente e quindi divisi nuovamente. Tutto questo "movimento" è anche molto complicato rispetto a una normale soluzione basata sull'espressione: anche se la velocità non ha importanza in una data applicazione, non è necessaria una soluzione complicata. Poiché il remodulo è standard e offre sia leggibilità che velocità, non vedo perché debba essere evitato.
Eric O Lebigot,

39

Suggerimento: utilizzare string.translate per le operazioni di stringa più veloci di Python.

Qualche prova ...

Innanzitutto, il modo lento (scusate 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

Successivamente, utilizziamo re.findall()(come indicato dalla risposta suggerita). Più veloce:

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

Infine, utilizziamo 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

Spiegazione:

string.translateè implementato in C e, diversamente da molte funzioni di manipolazione delle stringhe in Python, string.translate non produce una nuova stringa. Quindi è più veloce che puoi ottenere per la sostituzione di stringhe.

È un po 'imbarazzante, però, poiché ha bisogno di una tabella di traduzione per fare questa magia. È possibile creare una tabella di traduzione con la maketrans()funzione convenienza. L'obiettivo qui è quello di tradurre tutti i caratteri indesiderati in spazi. Un sostituto uno a uno. Ancora una volta, non vengono prodotti nuovi dati. Quindi è veloce !

Quindi, usiamo il buon vecchio split(). split()per impostazione predefinita funzionerà su tutti i caratteri degli spazi bianchi, raggruppandoli per la divisione. Il risultato sarà l'elenco delle parole che desideri. E questo approccio è quasi 4x più veloce di re.findall()!


4
Ho fatto un test qui, e se hai bisogno di usare unicode, usare patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)è più veloce di tradurre, perché devi codificare la stringa prima di applicare la trasformazione, e decodificare ogni elemento nell'elenco dopo la divisione per tornare a Unicode.
Rafael S. Calsaverini,

Puoi dare una sola spiegazione all'implementazione della traduzione e assicurarti che S non sia tra i divisori con:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
piani cottura

Nessuno preso. Stai confrontando mele e arance. ;) la mia soluzione in python 3 funziona ancora; P e ha il supporto per separatori multi-carattere. :) prova a farlo in modo semplice senza allocare una nuova stringa. :) ma vero, il mio è limitato all'analisi dei parametri della riga di comando e non ad un libro per esempio.
pprzemek,

dici "non produce una nuova stringa", nel senso che funziona all'interno di una determinata stringa? L'ho provato ora con Python 2.7 e non modifica la stringa originale e ne restituisce una nuova.
Prokop Hapala,

26

Avevo un dilemma simile e non volevo usare il modulo "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']

1
Mi piace questo. Solo una nota, l'ordine dei separatori è importante. Scusa se è ovvio.
crizCraig

2
Perché non usare il remodulo, che è sia più veloce che più chiaro (non che le espressioni regolari siano particolarmente chiare, ma perché è molto più breve e diretto)?
Eric O Lebigot,

13

In primo luogo, voglio essere d'accordo con gli altri sul fatto che la regex o le str.translate(...)soluzioni basate siano le più performanti. Nel mio caso d'uso le prestazioni di questa funzione non erano significative, quindi volevo aggiungere idee che consideravo con quei criteri.

Il mio obiettivo principale era quello di generalizzare idee da alcune delle altre risposte in un'unica soluzione che potesse funzionare per stringhe che contenessero più di semplici parole regex (ovvero, inserire nella lista nera il sottoinsieme esplicito di caratteri di punteggiatura rispetto a caratteri di parole nella whitelist).

Si noti che, in qualsiasi approccio, si potrebbe anche considerare l'utilizzo string.punctuational posto di un elenco definito manualmente.

Opzione 1 - re.sub

Sono stato sorpreso di non vedere nessuna risposta finora utilizza re.sub (...) . Lo trovo un approccio semplice e naturale a questo problema.

import re

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

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

In questa soluzione, ho annidato la chiamata re.sub(...)all'interno re.split(...)- ma se le prestazioni sono fondamentali, compilare la regex all'esterno potrebbe essere utile - per il mio caso d'uso, la differenza non era significativa, quindi preferisco semplicità e leggibilità.

Opzione 2 - sostituzione str

Si tratta di alcune righe in più, ma ha il vantaggio di essere espandibile senza dover verificare se è necessario sfuggire a un determinato personaggio in 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()

Sarebbe stato bello poter invece mappare str.replace sulla stringa, ma non penso che possa essere fatto con stringhe immutabili, e mentre la mappatura su un elenco di caratteri funzionerebbe, eseguendo ogni sostituzione su ogni carattere sembra eccessivo. (Modifica: vedere la prossima opzione per un esempio funzionale.)

Opzione 3 - functools.reduce

(In Python 2, reduceè disponibile nello spazio dei nomi globale senza importarlo da 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()

Hm, un altro metodo è quello di utilizzare str.translate- non è unicode ma molto probabilmente è più veloce di altri metodi e come tale potrebbe essere buono in alcuni casi: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))anche qui è obbligatorio avere sostituzioni come una stringa di caratteri, non tuple o elenco.
MarSoft,

@MarSoft Grazie! Ne ho parlato in cima alla risposta ma ho deciso di non aggiungerlo poiché le risposte esistenti ne hanno già discusso bene.
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]

Quindi questo diventa un tre-liner:

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

Spiegazione

Questo è ciò che in Haskell è noto come la monade List. L'idea alla base della monade è che una volta "nella monade" tu "rimani nella monade" fino a quando qualcosa ti porta fuori. Ad esempio, in Haskell, supponi di mappare la range(n) -> [1,2,...,n]funzione python su un elenco. Se il risultato è un elenco, verrà aggiunto all'elenco sul posto, in modo da ottenere qualcosa di simile map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Questo è noto come map-append (o mappend, o forse qualcosa del genere). L'idea qui è che hai questa operazione che stai applicando (suddividendo su un token) e ogni volta che lo fai, unisci il risultato all'elenco.

Puoi astrarre questo in una funzione e avere tokens=string.punctuationdi default.

Vantaggi di questo approccio:

  • Questo approccio (diversamente dagli approcci basati su regex ingenui) può funzionare con token di lunghezza arbitraria (che regex può fare anche con una sintassi più avanzata).
  • Non sei limitato ai semplici token; potresti avere una logica arbitraria al posto di ogni token, ad esempio uno dei "token" potrebbe essere una funzione che si divide in base alla parentesi annidata.

Soluzione ordinata di Haskell, ma IMO può essere scritta più chiaramente senza mappatura in Python.
Vlad l'Impala,

@Goose: il punto era che la funzione a 2 righe map_then_appendpuò essere utilizzata per rendere un problema un 2-liner, così come molti altri problemi molto più facili da scrivere. La maggior parte delle altre soluzioni utilizza il remodulo di espressione regolare , che non è Python. Ma non sono contento di come faccio sembrare la mia risposta inelegante e gonfia quando è davvero concisa ... Ho intenzione di modificarla ...
ninjagecko,

questo dovrebbe funzionare in Python come scritto? il mio fragmentsrisultato è solo un elenco dei caratteri nella stringa (inclusi i token).
Rick supporta Monica

@RickTeachey: funziona per me sia in python2 che in python3.
ninjagecko,

Hmmmm. Forse l'esempio è un po 'ambiguo. Ho provato il codice nella risposta tutti i tipi di diversi modi-compreso avere fragments = ['the,string'], fragments = 'the,string'o fragments = list('the,string')e nessuno di loro stanno producendo l'uscita giusta.
Rick supporta Monica

5

prova questo:

import re

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

questo stamperà ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']


4

Usa sostituisci due volte:

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

risulta in:

['11223', '33344', '33222', '3344']

4

Mi piace re , ma ecco la mia soluzione senza di essa:

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__ è un metodo utilizzato dall'operatore 'in'. Fondamentalmente è lo stesso di

lambda ch: ch in sep

ma è più conveniente qui.

groupby ottiene la nostra stringa e funzione. Suddivide la stringa in gruppi usando quella funzione: ogni volta che cambia un valore di funzione - viene generato un nuovo gruppo. Quindi, sep .__ contiene__ è esattamente ciò di cui abbiamo bisogno.

groupby restituisce una sequenza di coppie, in cui la coppia [0] è il risultato della nostra funzione e la coppia [1] è un gruppo. Usando 'if not k' filtriamo i gruppi con i separatori (perché un risultato di sep .__ contiene__ è True sui separatori). Bene, tutto qui - ora abbiamo una sequenza di gruppi in cui ognuno è una parola (gruppo è in realtà un iterabile, quindi usiamo join per convertirlo in stringa).

Questa soluzione è abbastanza generale, perché utilizza una funzione per separare la stringa (puoi dividerla per qualsiasi condizione di cui hai bisogno). Inoltre, non crea stringhe / elenchi intermedi (puoi rimuovere join e l'espressione diventerà pigra, poiché ogni gruppo è un iteratore)


4

Invece di utilizzare una funzione re module re.split puoi ottenere lo stesso risultato usando il metodo panda di series.str.split.

Innanzitutto, crea una serie con la stringa sopra e quindi applica il metodo alla serie.

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

Il parametro pat accetta i delimitatori e restituisce la stringa divisa come un array. Qui i due delimitatori vengono passati usando un | (o operatore). L'output è il seguente:

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


1
Non è una questione di dettaglio, ma piuttosto il fatto di importare un'intera libreria (che adoro, a proposito) per eseguire un semplice compito dopo aver convertito una stringa in una serie di panda. Non molto & quot; Occam friendly & quot ;.
zar3bski,

3

Sto conoscendo me stesso con Python e avevo bisogno della stessa cosa. La soluzione findall potrebbe essere migliore, ma ho pensato a questo:

tokens = [x.strip() for x in data.split(',')]

Intelligente, dovrebbe funzionare su tutti i costrutti grammaticali inglesi a cui riesco a pensare tranne un trattino senza spazi: questo, ad esempio. (Soluzione alternativa.)
ninjagecko,

3

usando maketrans e traducendo puoi farlo facilmente e ordinatamente

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()

Ottima risposta per Python> = 3.6
revliscano il

3

In Python 3, puoi usare il metodo di PY4E - Python per tutti .

Siamo in grado di risolvere entrambi questi problemi utilizzando i metodi delle stringhe lower, punctuatione translate. Il translateè il metodo più sottile. Ecco la documentazione per translate:

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

Sostituisci i caratteri fromstrcon il personaggio nella stessa posizione in tostred elimina tutti i caratteri presenti deletestr. La fromstre tostrpossono essere stringhe vuote e il deletestrparametro può essere omesso.

Puoi vedere la "punteggiatura":

In [10]: import string

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

Per il tuo esempio:

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

Per ulteriori informazioni, è possibile fare riferimento a:


2
I metodi di stringhe translate () e maketrans () sono interessanti, ma questo metodo non riesce a "dividere a delimitatori" (o spazi bianchi): per esempio, "C'era una grande caverna" produrrà erroneamente la parola "cavein" della "caverna" attesa e "in" ... Quindi, questo non fa ciò che la domanda chiede.
Eric O Lebigot,

Proprio come ha commentato @EricLebigot. Il metodo sopra riportato non fa molto bene la domanda.
Jeremy Anifacc,

2

Un altro modo per raggiungere questo obiettivo è utilizzare il Natural Language Tool Kit ( nltk ).

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

Questo stampa: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Il più grande svantaggio di questo metodo è che è necessario installare il pacchetto nltk .

I vantaggi sono che puoi fare un sacco di cose divertenti con il resto del pacchetto nltk una volta ottenuti i token.


1

Prima di tutto, non penso che la tua intenzione sia quella di usare effettivamente la punteggiatura come delimitatori nelle funzioni split. La tua descrizione suggerisce che desideri semplicemente eliminare la punteggiatura dalle stringhe risultanti.

Mi imbatto molto spesso e la mia solita soluzione non richiede ri.

Funzione lambda di una riga con comprensione dell'elenco:

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


Funzione (tradizionale)

Come funzione tradizionale, sono ancora solo due righe con una comprensione dell'elenco (oltre a 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']

Inoltre, lascerà naturalmente intatte le contrazioni e le parole sillabate. Puoi sempre usare text.replace("-", " ")per trasformare i trattini in spazi prima della divisione.

Funzione generale senza Lambda o Comprensione elenco

Per una soluzione più generale (in cui è possibile specificare i caratteri da eliminare) e senza una comprensione dell'elenco, si ottiene:

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

Naturalmente, puoi sempre generalizzare la funzione lambda anche a qualsiasi stringa di caratteri specificata.


1

Prima di tutto, usa sempre re.compile () prima di eseguire qualsiasi operazione RegEx in un ciclo perché funziona più velocemente del normale funzionamento.

quindi per il tuo problema prima compila il pattern e poi esegui un'azione su di esso.

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

1

Ecco la risposta con qualche spiegazione.

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 in una riga, possiamo fare così:

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

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

risposta aggiornata


1

Creare una funzione che accetta come input due stringhe (la stringa di origine da dividere e la stringa di delimitatori della lista divisa) e genera un elenco di parole divise:

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

Mi piace la soluzione di pprzemek perché non presuppone che i delimitatori siano singoli caratteri e non cerca di sfruttare una regex (che non funzionerebbe bene se il numero di separatori diventasse pazzo a lungo).

Ecco una versione più leggibile della soluzione sopra per chiarezza:

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

0

ha lo stesso problema di @ooboo e trova questo argomento @ ghostdog74 che mi ha ispirato, forse qualcuno trova utile la mia soluzione

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

inserisci qualcosa nello spazio e dividi usando lo stesso carattere se non vuoi dividere gli spazi.


cosa succede se devo dividere usando word?
Harsha Biyani,

0

Ecco il mio passaggio a una divisione con più deliminaters:

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

Penso che la seguente sia la migliore risposta per soddisfare le tue esigenze:

\W+ forse adatto per questo caso, ma potrebbe non essere adatto per altri casi.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")

Sono d'accordo, le soluzioni \we \Wnon sono una risposta al (titolo della) domanda. Nota che nella tua risposta, |dovrebbe essere rimosso (stai pensando expr0|expr1invece di [char0 char1…]). Inoltre, non è necessario compile()l'espressione regolare.
Eric O Lebigot,

0

Ecco la mia opinione su di esso ....

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

Mi piace il replace()modo migliore. La seguente procedura modifica tutti i separatori definiti in una stringa splitlistnel primo separatore in splitliste quindi divide il testo su quel separatore. Tiene inoltre conto se splitlistsi tratta di una stringa vuota. Restituisce un elenco di parole, senza stringhe vuote.

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

Ecco l'uso:

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

0

Se si desidera un'operazione reversibile (preservare i delimitatori), è possibile utilizzare questa funzione:

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

0

Di recente avevo bisogno di farlo, ma volevo una funzione che corrispondesse in qualche modo alla str.splitfunzione di libreria standard , questa funzione si comporta come la libreria standard quando viene chiamata con argomenti 0 o 1.

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 : questa funzione è utile solo quando i separatori sono costituiti da un singolo carattere (come è stato il mio caso d'uso).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.