Dividir seqüências de caracteres em palavras com vários delimitadores de limite de palavras

670

Acho que o que quero fazer é uma tarefa bastante comum, mas não encontrei nenhuma referência na web. Tenho texto com pontuação e quero uma lista de palavras.

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

deveria estar

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

Mas o Python str.split()funciona apenas com um argumento, então eu tenho todas as palavras com a pontuação depois de dividir com espaço em branco. Alguma ideia?

ooboo
fonte
6
o python str.split()também funciona sem argumentos
Ivan Vinogradov 8/18

Respostas:

467

Um caso em que expressões regulares são justificadas:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
RichieHindle
fonte
2
Obrigado. Ainda estou interessado - como posso implementar o algoritmo usado neste módulo? E por que não aparece no módulo de string?
29509 ooboo
29
Expressões regulares podem ser assustadoras no começo, mas são muito poderosas. A expressão regular '\ w +' significa "um caractere de palavra (az etc.) repetido uma ou mais vezes". Há um tutorial sobre Python expressões regulares aqui: amk.ca/python/howto/regex
RichieHindle
324
Esta não é a resposta para a pergunta. Esta é uma resposta para uma pergunta diferente, que funciona para essa situação específica. É como se alguém perguntasse "como faço para virar à esquerda" e a resposta mais votada fosse "faça as próximas três voltas à direita". Funciona para determinadas interseções, mas não fornece a resposta necessária. Ironicamente, a resposta é em re, simplesmente não findall. A resposta abaixo dando re.split()é superior.
Jesse Dhillon
4
@JesseDhillon "pega todas as substrings que consistem em uma sequência de caracteres de palavras" e "divide em todas as substrings que consistem em uma sequência de caracteres que não são palavras" são literalmente apenas maneiras diferentes de expressar a mesma operação; Não sei por que você chamaria uma resposta superior.
Mark-Amery
4
@TMWP: O apóstolo significa que uma palavra como don'té tratada como uma única palavra, em vez de ser dividida em done t.
precisa saber é o seguinte
574

re.split ()

re.split (padrão, string [, maxsplit = 0])

Divida a string pelas ocorrências do padrão. Se a captura de parênteses for usada no padrão, o texto de todos os grupos no padrão também será retornado como parte da lista resultante. Se maxsplit for diferente de zero, ocorrerão no máximo divisões de maxsplit e o restante da sequência será retornado como o elemento final da lista. (Nota sobre incompatibilidade: na versão original do Python 1.5, o maxsplit foi ignorado. Isso foi corrigido em versões posteriores.)

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
gimel
fonte
13
Essa solução tem a vantagem de ser facilmente adaptada para dividir também em sublinhados, algo que a solução findall não possui: print re.split ("\ W + | _", "Testing this_thing") 'produz: [' Testing ',' this ' , 'coisa']
Emil Stenström
63
Agora, se eu pudesse lembrar a diferença entre \w, \W, \s, e \S. Quem pensou que a capitalização de uma bandeira deveria inverter seu significado precisa ser atingido na cabeça.
ArtOfWarfare 17/07/2015
1
Um caso de uso comum de divisão de cadeia é remover entradas de cadeia vazias do resultado final. É possível fazer isso com esse método? re.split ('\ W +', 'abc') resulta em ['', 'a', 'b', 'c', '']
Scott Morken
3
@ArtOfWarfare É comum usar a shiftchave para fazer o oposto de algo. ctrl+zdesfazer vs. ctrl+shift+zrefazer. Então shift w, ou W, seria o oposto de w.
22818 Frank Vel
1
Esta resposta deve estar no topo - é a única que responde com precisão o título da pergunta.
Kranach
380

Outra maneira rápida de fazer isso sem uma regexp é substituir os caracteres primeiro, como abaixo:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']
Louis LC
fonte
71
Rápido e sujo, mas perfeito para o meu caso (meus separadores foram um pequeno conjunto conhecido)
Andy Baker
7
Perfeito para o caso em que você não tem acesso à biblioteca RE, como certos pequenos microcontroladores. :-)
tu-Restabelece Monica-dor duh
11
Eu acho que isso é mais explícito do que o ER também, então é meio amigável. Às vezes não precisa de solução geral para tudo
Adam Hughes
Impressionante. Eu tinha um .split () em uma situação de várias entradas e precisava capturar quando o usuário, eu, separamos as entradas com um espaço e não uma vírgula. Eu estava prestes a desistir e reformular com re, mas sua solução .replace () atingiu a unha na cabeça. Obrigado.
JayJay123
você receberá uma resposta errada quando não desejar dividir espaços e em outros caracteres.
Ahmed Amr
307

Tantas respostas, mas não consigo encontrar nenhuma solução que faça com eficiência o que o título das perguntas pede literalmente (dividindo-se em vários separadores possíveis - em vez disso, muitas respostas se dividem em qualquer coisa que não seja uma palavra, que seja diferente). Então, aqui está uma resposta para a pergunta no título, que se baseia no remódulo padrão e eficiente do 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']

Onde:

  • as […]partidas um dos separadores referidos, dentro
  • a \-expressão regular está aqui para impedir a interpretação especial de -como um indicador de intervalo de caracteres (como em A-Z),
  • os +saltos um ou mais delimitadores (que poderia ser omitida, graças àfilter() , mas isso iria desnecessariamente produzir cadeias vazias entre separadores correspondentes), e
  • filter(None, …) remove as cadeias vazias, possivelmente criadas pelos separadores à esquerda e à direita (já que as cadeias vazias têm um valor booleano falso).

Isso re.split()"se divide precisamente com vários separadores", conforme solicitado no título da pergunta.

Além disso, esta solução é imune aos problemas com caracteres não ASCII em palavras encontradas em outras soluções (consulte o primeiro comentário à resposta de ghostdog74 ).

O remódulo é muito mais eficiente (em velocidade e concisão) do que fazer loops e testes em Python "à mão"!

Eric O Lebigot
fonte
3
"Não consigo encontrar nenhuma solução que faça com eficiência o que o título das perguntas literalmente pede" - a segunda resposta faz isso, publicada há 5 anos: stackoverflow.com/a/1059601/2642204 .
BartoszKP 02
17
Esta resposta não é dividida em delimitadores (de um conjunto de vários delimitadores): em vez disso, é dividida em qualquer coisa que não seja alfanumérica. Dito isto, concordo que a intenção do pôster original é provavelmente manter apenas as palavras, em vez de remover alguns sinais de pontuação.
Eric O Lebigot
EOL: Eu acho que essa resposta se divide em um conjunto de vários delímetros. Se você incluir caracteres não alfanuméricos na sequência que não está especificada, como sublinhado, eles não serão divididos conforme o esperado.
precisa saber é o seguinte
@ GravityWell: Não sei ao certo: você pode dar um exemplo concreto?
Eric O Lebigot
3
@EOL: Acabei de perceber que fiquei confusa com o seu comentário "Esta resposta não se divide ..." Pensei que "isso" se referisse à sua resposta re.split, mas agora percebo que você quis dizer a resposta de gimel. Eu acho que esta resposta (a resposta para o que eu estou comentando) é a melhor resposta :)
GravityWell
56

Outra maneira, sem 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
fonte
8
Esta solução é realmente melhor que a aceita. Funciona sem caracteres ASCII, tente "Hey, you - what are you doing here María!?". A solução aceita não funcionará com o exemplo anterior.
Christopher Ramírez
4
Acho que há um pequeno problema aqui ... Seu código irá acrescentar personagens que são separados com a pontuação e, portanto, não irá dividir-los ... Se não me engano, a sua última linha deve ser:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu
A biblioteca de expressões regulares pode ser criada para aceitar convenções Unicode para caracteres, se necessário. Além disso, isso tem o mesmo problema que a solução aceita costumava ter: como é agora, divide-se em apóstrofes. Você pode querer o for o in s if (o in not string.punctuation or o == "'"), mas está ficando muito complicado para uma linha, se adicionarmos também o patch da cedbeu.
Daniel H
Há outra questão aqui. Mesmo quando levamos em conta as alterações do @cedbeu, esse código não funciona se a string for algo parecido "First Name,Last Name,Street Address,City,State,Zip Code"e queremos dividir apenas por vírgula ,. Saída desejada seria: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']O que temos em vez disso:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Stefan van den Akker
4
Essa solução é terrivelmente ineficiente: primeiro a lista é desconstruída em caracteres individuais, depois todo o conjunto de caracteres de pontuação é passado para cada caractere único na sequência original, depois os caracteres são reunidos novamente e depois divididos novamente. Todo esse "movimento" também é muito complicado, comparado a uma solução baseada em expressões regulares: mesmo que a velocidade não importe em um determinado aplicativo, não há necessidade de uma solução complicada. Como o remódulo é padrão e oferece legibilidade e velocidade, não vejo por que ele deve ser evitado.
Eric O Lebigot
39

Dica profissional: use string.translate para as operações mais rápidas de strings que o Python possui.

Alguma prova ...

Primeiro, o caminho lento (desculpe 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

Em seguida, usamos re.findall()(conforme indicado pela resposta sugerida). Muito mais rapido:

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

Finalmente, usamos translate:

>>> from string import translate,maketrans,punctuation 
>>> T = maketrans(punctuation, ' '*len(punctuation))
>>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit()
1.2835021018981934

Explicação:

string.translateé implementado em C e, diferentemente de muitas funções de manipulação de strings no Python, string.translate não produz uma nova string. Portanto, é o mais rápido possível para a substituição de strings.

É um pouco estranho, porém, pois precisa de uma tabela de tradução para fazer essa mágica. Você pode criar uma tabela de conversão com a maketrans()função de conveniência. O objetivo aqui é converter todos os caracteres indesejados em espaços. Um substituto um por um. Novamente, nenhum novo dado é produzido. Então isso é rápido !

Em seguida, usamos o bom e velho split(). split()por padrão, operará em todos os caracteres de espaço em branco, agrupando-os para a divisão. O resultado será a lista de palavras que você deseja. E essa abordagem é quase 4x mais rápida que re.findall()!

Dave
fonte
4
Fiz um teste aqui e, se você precisar usar o unicode, usar patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)é mais rápido que traduzir, porque você deve codificar a sequência antes de aplicar a transformação e decodificar cada item da lista após a divisão para voltar ao unicode.
Rafael S. Calsaverini 15/01
Você pode one-liner a implementação traduzir e garantir que S não está entre os divisores com:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
Placas
Nenhuma tomada. Você está comparando maçãs e laranjas. ;) minha solução em python 3 ainda funciona; P e tem suporte para separadores de vários caracteres. :) tente fazer isso de maneira simples sem alocar uma nova string. :) mas é verdade, o meu é limitado a analisar parâmetros de linha de comando e não um livro, por exemplo.
pprzemek
você diz "não produz uma nova string", o que significa que funciona no local em uma determinada string? Eu testei agora com python 2.7 e ele não modifica a string oroginal e retorna uma nova.
Prokop Hapala 15/09/19
26

Eu tinha um dilema semelhante e não queria usar o módulo 're'.

def my_split(s, seps):
    res = [s]
    for sep in seps:
        s, res = res, []
        for seq in s:
            res += seq.split(sep)
    return res

print my_split('1111  2222 3333;4444,5555;6666', [' ', ';', ','])
['1111', '', '2222', '3333', '4444', '5555', '6666']
pprzemek
fonte
1
Eu gosto disso. Apenas uma nota, a ordem dos separadores é importante. Desculpe se isso é óbvio.
crizCraig
2
Por que não usar o remódulo, que é mais rápido e mais claro (não que expressões regulares sejam especialmente claras, mas porque é muito mais curta e direta)?
Eric O Lebigot
13

Primeiro, quero concordar com os outros que o regex ou as str.translate(...)soluções baseadas são de melhor desempenho. Para o meu caso de uso, o desempenho dessa função não foi significativo, então eu queria adicionar idéias que considerasse com esse critério.

Meu principal objetivo era generalizar idéias de algumas das outras respostas em uma solução que pudesse funcionar para seqüências que contenham mais do que apenas palavras regex (ou seja, colocar na lista negra o subconjunto explícito de caracteres de pontuação versus caracteres de palavra na lista de permissões).

Observe que, em qualquer abordagem, também se pode considerar o uso string.punctuationno lugar de uma lista definida manualmente.

Opção 1 - re.sub

Fiquei surpreso ao ver que nenhuma resposta até agora usa re.sub (...) . Acho que é uma abordagem simples e natural para esse problema.

import re

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

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

Nesta solução, aninhei a chamada para re.sub(...)dentro re.split(...)- mas se o desempenho for crítico, compilar o regex fora pode ser benéfico - para o meu caso de uso, a diferença não foi significativa, portanto prefiro simplicidade e legibilidade.

Opção 2 - str.replace

São mais algumas linhas, mas tem o benefício de ser expansível sem precisar verificar se você precisa escapar de um determinado caractere na 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()

Teria sido bom poder mapear o str.replace para a string, mas não acho que isso possa ser feito com strings imutáveis, e o mapeamento de uma lista de caracteres funcionaria, executando todas as substituições de cada personagem parece excessivo. (Editar: veja a próxima opção para um exemplo funcional.)

Opção 3 - functools.reduce

(No Python 2, reduceestá disponível no espaço de nomes global sem importá-lo das funções de ferramenta.)

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
fonte
Hum, um outro método é usar str.translate- ele não é capaz de unicode, mas é provavelmente mais rápido que outros métodos e, como tal, pode ser bom em alguns casos: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))Também aqui é obrigatório ter substituições como uma sequência de caracteres, não tupla ou Lista.
MarSoft
@MarSoft Thanks! Mencionei aquele no topo da resposta, mas decidi não adicioná-lo, já que as respostas existentes já o discutiam bem.
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]

Em seguida, isso se torna uma lista de três linhas:

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

Explicação

É isso que em Haskell é conhecido como mônada da lista. A idéia por trás da mônada é que uma vez "na mônada" você "fica na mônada" até que alguma coisa o tire. Por exemplo, em Haskell, digamos que você mapeie a range(n) -> [1,2,...,n]função python sobre uma lista. Se o resultado for uma Lista, ela será anexada à Lista no local, para que você obtenha algo parecido map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Isso é conhecido como anexo de mapa (ou mappend, ou talvez algo parecido). A idéia aqui é que você tenha essa operação que está aplicando (dividindo em um token) e, sempre que fizer isso, junte o resultado à lista.

Você pode abstrair isso em uma função e ter tokens=string.punctuationpor padrão.

Vantagens dessa abordagem:

  • Essa abordagem (diferente das abordagens ingênuas baseadas em regex) pode funcionar com tokens de tamanho arbitrário (que regex também pode fazer com sintaxe mais avançada).
  • Você não está restrito a meros tokens; você pode ter uma lógica arbitrária no lugar de cada token, por exemplo, um dos "tokens" pode ser uma função que se divide de acordo com a parênteses aninhados.
ninjagecko
fonte
Solução pura de Haskell, mas na IMO isso pode ser escrito com mais clareza sem mappend no Python.
Vlad, o Impala,
@ Goose: o ponto era que a função de 2 linhas map_then_appendpode ser usada para transformar um problema em 2 linhas , assim como muitos outros problemas muito mais fáceis de escrever. A maioria das outras soluções usa o remódulo de expressão regular , que não é python. Mas eu tenho sido infeliz com a forma como eu faço a minha resposta parece deselegante e bloaty quando é realmente concisa ... Eu estou indo para editá-lo ...
ninjagecko
isso deveria estar funcionando no Python como escrito? meu fragmentsresultado é apenas uma lista dos caracteres da string (incluindo os tokens).
Rick suporta Monica
@ RickTeachey: funciona para mim em python2 e python3.
Ninjagecko 17/05
hmmmm. Talvez o exemplo seja um pouco ambíguo. Eu tentei o código na resposta todos os tipos de maneiras-diferente incluindo ter fragments = ['the,string'], fragments = 'the,string'ou fragments = list('the,string')e nenhum deles estão produzindo a saída direita.
Rick apoia Monica
5

tente isto:

import re

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

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

Corey Goldberg
fonte
4

Use substituir duas vezes:

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

resulta em:

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

Eu gosto de re , mas aqui está a minha solução sem ela:

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__ é um método usado pelo operador 'in'. Basicamente, é o mesmo que

lambda ch: ch in sep

mas é mais conveniente aqui.

groupby obtém nossa string e função. Ele divide a string em grupos usando essa função: sempre que um valor da função é alterado - um novo grupo é gerado. Então, set .__ contém__ é exatamente o que precisamos.

groupby retorna uma sequência de pares, onde o par [0] é o resultado de nossa função e o par [1] é um grupo. Usando 'if not k' , filtramos grupos com separadores (porque o resultado de set .__ contains__ é True nos separadores). Bem, isso é tudo - agora temos uma sequência de grupos em que cada um é uma palavra (o grupo é realmente uma iterável, então usamos join para convertê-lo em string).

Essa solução é bastante geral, porque usa uma função para separar a string (você pode dividir por qualquer condição que precisar). Além disso, ele não cria cadeias / listas intermediárias (você pode remover a junção e a expressão se tornará preguiçosa, pois cada grupo é um iterador)

monitorius
fonte
4

Em vez de usar a função re module re.split, você pode obter o mesmo resultado usando o método series.str.split dos pandas.

Primeiro, crie uma série com a sequência acima e aplique o método à série.

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

O parâmetro pat pega os delimitadores e retorna a cadeia de divisão como uma matriz. Aqui os dois delimitadores são passados ​​usando um | (ou operador). A saída é a seguinte:

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

Tarun Kumar Yellapu
fonte
1
Não é uma questão detalhada, mas sim o fato de importar uma biblioteca inteira (que eu amo, BTW) para executar uma tarefa simples depois de converter uma string em uma série panda. Não é muito "amigável ao Occam".
zar3bski
3

Estou me familiarizando com o Python e precisava da mesma coisa. A solução findall pode ser melhor, mas eu vim com isso:

tokens = [x.strip() for x in data.split(',')]
Leon Starr
fonte
Inteligente, deve funcionar em todas as construções gramaticais inglesas em que consigo pensar, exceto em um traço sem espaços - isso, por exemplo. (Solução alternativa.)
ninjagecko
3

usando maketrans e traduza você pode fazê-lo facilmente e ordenadamente

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
Ritesh Sinha
fonte
Ótima resposta quanto ao Python> = 3.6
revliscano
3

No Python 3, você pode usar o método PY4E - Python for Everybody .

Podemos resolver ambos os problemas usando os métodos de cordas lower, punctuatione translate. O translateé o mais sutil dos métodos. Aqui está a documentação para translate:

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

Substitua os personagens fromstrcom o personagem na mesma posição em tostre excluir todos os caracteres que estão em deletestr. As cadeias fromstre tostrpodem ser vazias e o deletestrparâmetro pode ser omitido.

Você pode ver a "pontuação":

In [10]: import string

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

Para o seu exemplo:

In [12]: your_str = "Hey, you - what are you doing here!?"

In [13]: line = your_str.translate(your_str.maketrans('', '', string.punctuation))

In [14]: line = line.lower()

In [15]: words = line.split()

In [16]: print(words)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Para mais informações, você pode consultar:

Jeremy Anifacc
fonte
2
Os métodos translate () e maketrans () de strings são interessantes, mas esse método falha ao "dividir em delimitadores" (ou espaço em branco): por exemplo, "Houve uma grande invasão" produzirá incorretamente a palavra "cavein" da "caverna" esperada e "em" ... Assim, isso não faz o que a pergunta pede.
Eric O Lebigot
Assim como o @EricLebigot comentou. O método acima não faz o que a pergunta pede muito bem.
Jeremy Anifacc
2

Outra maneira de conseguir isso é usar o 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

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

A maior desvantagem desse método é que você precisa instalar o pacote nltk .

Os benefícios são que você pode fazer muitas coisas divertidas com o restante do pacote nltk depois de receber seus tokens.

tgray
fonte
1

Primeiro de tudo, não acho que sua intenção seja realmente usar pontuação como delimitadores nas funções de divisão. Sua descrição sugere que você simplesmente deseja eliminar a pontuação das seqüências de caracteres resultantes.

Eu me deparei com isso com bastante frequência, e minha solução usual não requer re.

Função lambda de uma linha com compreensão de lista:

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


Função (tradicional)

Como uma função tradicional, ainda são apenas duas linhas com uma compreensão da lista (além 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']

Também deixará naturalmente as contrações e as palavras hifenizadas intactas. Você sempre pode usar text.replace("-", " ")para transformar hífens em espaços antes da divisão.

Função geral sem Lambda ou compreensão de lista

Para uma solução mais geral (onde você pode especificar os caracteres a serem eliminados) e sem uma compreensão da lista, você obtém:

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

Obviamente, você sempre pode generalizar a função lambda para qualquer sequência de caracteres especificada.

cosmicFluke
fonte
1

Antes de tudo, sempre use re.compile () antes de executar qualquer operação RegEx em um loop, pois funciona mais rápido que a operação normal.

portanto, para o seu problema, primeiro compile o padrão e execute uma ação nele.

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

Aqui está a resposta com alguma explicação.

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 em uma linha, podemos fazer assim:

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

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

resposta atualizada

Tasneem Haider
fonte
1

Crie uma função que tenha como entrada duas cadeias (a cadeia de origem a ser dividida e a cadeia de delimitadores da lista de divisão) e produz uma lista de palavras divididas:

def split_string(source, splitlist):
    output = []  # output list of cleaned words
    atsplit = True
    for char in source:
        if char in splitlist:
            atsplit = True
        else:
            if atsplit:
                output.append(char)  # append new word after split
                atsplit = False
            else: 
                output[-1] = output[-1] + char  # continue copying characters until next split
    return output
user852006
fonte
1

Gosto da solução do pprzemek porque ele não supõe que os delimitadores sejam caracteres únicos e não tenta alavancar um regex (o que não funcionaria bem se o número de separadores ficasse louco por muito tempo).

Aqui está uma versão mais legível da solução acima para maior clareza:

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

tenho o mesmo problema que @ooboo e encontre este tópico @ ghostdog74 me inspirou, talvez alguém ache minha solução útil

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

insira algo no espaço e divida usando o mesmo caractere se você não quiser dividir em espaços.

badas
fonte
e se eu tiver que dividir usando o word?
Harsha Biyani
0

Aqui está a minha chance de dividir com vários deliminadores:

def msplit( str, delims ):
  w = ''
  for z in str:
    if z not in delims:
        w += z
    else:
        if len(w) > 0 :
            yield w
        w = ''
  if len(w) > 0 :
    yield w
Martlark
fonte
0

Eu acho que a seguinte é a melhor resposta para atender às suas necessidades:

\W+ talvez adequado para este caso, mas pode não ser adequado para outros casos.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")
nemozhp
fonte
Eu concordo, as soluções \we \Wnão são uma resposta para (o título) da pergunta. Observe que na sua resposta, |deve ser removido (você está pensando em expr0|expr1vez de [char0 char1…]). Além disso, não há necessidade compile()da expressão regular.
Eric O Lebigot
0

Heres minha opinião sobre isso ....

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

Eu gosto replace()da melhor maneira. O procedimento a seguir altera todos os separadores definidos em uma seqüência splitlistde caracteres para o primeiro separador splitliste, em seguida, divide o texto nesse separador. Ele também explica se splitlisté uma string vazia. Retorna uma lista de palavras, sem cadeias vazias.

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

Aqui está o uso:

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

Se você deseja uma operação reversível (preserve os delimitadores), poderá usar esta função:

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

Recentemente, eu precisava fazer isso, mas queria uma função que correspondesse à str.splitfunção da biblioteca padrão ; essa função se comporta da mesma forma que a biblioteca padrão quando chamada com argumentos 0 ou 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 : Essa função é útil apenas quando seus separadores consistem em um único caractere (como foi meu caso de usuário).

Justin Fay
fonte