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?
str.split()
também funciona sem argumentosRespostas:
Um caso em que expressões regulares são justificadas:
fonte
re
, simplesmente nãofindall
. A resposta abaixo dandore.split()
é superior.don't
é tratada como uma única palavra, em vez de ser dividida emdon
et
.re.split ()
fonte
\w
,\W
,\s
, e\S
. Quem pensou que a capitalização de uma bandeira deveria inverter seu significado precisa ser atingido na cabeça.shift
chave para fazer o oposto de algo.ctrl+z
desfazer vs.ctrl+shift+z
refazer. Entãoshift w
, ouW
, seria o oposto dew
.Outra maneira rápida de fazer isso sem uma regexp é substituir os caracteres primeiro, como abaixo:
fonte
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
re
módulo padrão e eficiente do Python :Onde:
[…]
partidas um dos separadores referidos, dentro\-
expressão regular está aqui para impedir a interpretação especial de-
como um indicador de intervalo de caracteres (como emA-Z
),+
saltos um ou mais delimitadores (que poderia ser omitida, graças àfilter()
, mas isso iria desnecessariamente produzir cadeias vazias entre separadores correspondentes), efilter(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
re
módulo é muito mais eficiente (em velocidade e concisão) do que fazer loops e testes em Python "à mão"!fonte
Outra maneira, sem regex
fonte
"Hey, you - what are you doing here María!?"
. A solução aceita não funcionará com o exemplo anterior.''.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 == "'")
, mas está ficando muito complicado para uma linha, se adicionarmos também o patch da cedbeu."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']
re
módulo é padrão e oferece legibilidade e velocidade, não vejo por que ele deve ser evitado.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):
Em seguida, usamos
re.findall()
(conforme indicado pela resposta sugerida). Muito mais rapido:Finalmente, usamos
translate
: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 quere.findall()
!fonte
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.s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
Eu tinha um dilema semelhante e não queria usar o módulo 're'.
fonte
re
módulo, que é mais rápido e mais claro (não que expressões regulares sejam especialmente claras, mas porque é muito mais curta e direta)?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.punctuation
no 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.
Nesta solução, aninhei a chamada para
re.sub(...)
dentrore.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.
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,
reduce
está disponível no espaço de nomes global sem importá-lo das funções de ferramenta.)fonte
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.Em seguida, isso se torna uma lista de três linhas:
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 parecidomap(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.punctuation
por padrão.Vantagens dessa abordagem:
fonte
map_then_append
pode 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 ore
mó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 ...fragments
resultado é apenas uma lista dos caracteres da string (incluindo os tokens).fragments = ['the,string']
,fragments = 'the,string'
oufragments = list('the,string')
e nenhum deles estão produzindo a saída direita.tente isto:
isso imprimirá
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
fonte
Use substituir duas vezes:
resulta em:
fonte
Eu gosto de re , mas aqui está a minha solução sem ela:
sep .__ contains__ é um método usado pelo operador 'in'. Basicamente, é o mesmo que
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)
fonte
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!?]
fonte
Estou me familiarizando com o Python e precisava da mesma coisa. A solução findall pode ser melhor, mas eu vim com isso:
fonte
usando maketrans e traduza você pode fazê-lo facilmente e ordenadamente
fonte
No Python 3, você pode usar o método PY4E - Python for Everybody .
your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))
Você pode ver a "pontuação":
Para o seu exemplo:
Para mais informações, você pode consultar:
fonte
Outra maneira de conseguir isso é usar o Natural Language Tool Kit ( nltk ).
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.
fonte
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
):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
):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:
Obviamente, você sempre pode generalizar a função lambda para qualquer sequência de caracteres especificada.
fonte
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.
fonte
Aqui está a resposta com alguma explicação.
ou em uma linha, podemos fazer assim:
resposta atualizada
fonte
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:
fonte
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:
fonte
tenho o mesmo problema que @ooboo e encontre este tópico @ ghostdog74 me inspirou, talvez alguém ache minha solução útil
insira algo no espaço e divida usando o mesmo caractere se você não quiser dividir em espaços.
fonte
Aqui está a minha chance de dividir com vários deliminadores:
fonte
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.fonte
\w
e\W
não são uma resposta para (o título) da pergunta. Observe que na sua resposta,|
deve ser removido (você está pensando emexpr0|expr1
vez de[char0 char1…]
). Além disso, não há necessidadecompile()
da expressão regular.Heres minha opinião sobre isso ....
fonte
Eu gosto
replace()
da melhor maneira. O procedimento a seguir altera todos os separadores definidos em uma seqüênciasplitlist
de caracteres para o primeiro separadorsplitlist
e, em seguida, divide o texto nesse separador. Ele também explica sesplitlist
é uma string vazia. Retorna uma lista de palavras, sem cadeias vazias.fonte
Aqui está o uso:
fonte
Se você deseja uma operação reversível (preserve os delimitadores), poderá usar esta função:
fonte
Recentemente, eu precisava fazer isso, mas queria uma função que correspondesse à
str.split
funçã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.NOTA : Essa função é útil apenas quando seus separadores consistem em um único caractere (como foi meu caso de usuário).
fonte