Por que as strings vazias são retornadas nos resultados de split ()?

120

Qual é o ponto de '/segment/segment/'.split('/')voltar ['', 'segment', 'segment', '']?

Observe os elementos vazios. Se você estiver dividindo em um delimitador que por acaso está na posição um e no final de uma string, que valor extra isso dá para ter a string vazia retornada de cada extremidade?

orokusaki
fonte
1
Eu tenho a mesma dúvida e procurei por muito tempo. Agora eu entendo que os resultados vazios são realmente importantes. Obrigado pela sua pergunta.
emeraldhieu,
2
Uma solução é strip()retirar os caracteres de divisão '/segment/segment/'.strip('/').split('/')
iniciais

Respostas:

178

str.splitcomplementos str.join, então

"/".join(['', 'segment', 'segment', ''])

obtém de volta a string original.

Se as strings vazias não estivessem lá, a primeira e a última '/'estariam faltando após ojoin()

John La Rooy
fonte
11
Simples, mas responde totalmente à pergunta.
orokusaki
Fiquei chocado ao descobrir que as aspas curvas são realmente válidas em Python ... mas, mas ... como? Os documentos parecem não mencionar isso.
Tim Pietzcker,
@Tim, não tenho ideia de como essas citações chegaram lá: /
John La Rooy
7
Então, você não está usando o Microsoft Word como seu IDE Python? :)
Tim Pietzcker,
1
@ aaa90210 que disse que respostas simples não eram as melhores? Foi um comentário (em primeiro lugar, 5 anos atrás) sobre como a resposta era simples, mas respondia totalmente à pergunta. Usar "mas" em uma frase não implica nada de ruim. Uma resposta não simples pode ter sido uma resposta mais completa (por exemplo, incluindo decisões relevantes ou um PEP relacionado à funcionalidade observada).
orokusaki
88

De forma mais geral, para remover strings vazias retornadas nos split()resultados, você pode querer examinar a filterfunção.

Exemplo:

filter(None, '/segment/segment/'.split('/'))

retorna

['segment', 'segment']
Franck Dernoncourt
fonte
3
Obrigado por isso, não sei por que essa resposta está tão baixa, todo o resto é coisa rudimentar.
Quarta-
6
Se desejar coletar o resultado em uma lista em vez de obter um objeto de filtro como saída, coloque toda a estrutura do filtro list(...).
Tim Visée de
29

Existem dois pontos principais a serem considerados aqui:

  • Esperar que o resultado de '/segment/segment/'.split('/')seja igual a ['segment', 'segment']é razoável, mas isso perde informações. Se split()funcionou do jeito que você queria, se eu disser isso a.split('/') == ['segment', 'segment'], você não pode me dizer o que afoi.
  • Qual deve ser o resultado de 'a//b'.split()ser? ['a', 'b']?, ou ['a', '', 'b']? Ou seja, deve split()mesclar delimitadores adjacentes? Se for o caso, será muito difícil analisar dados delimitados por um caractere e alguns dos campos podem estar vazios. Estou bastante certo de que há muitas pessoas que fazem que os valores vazios no resultado para o caso acima!

No final, tudo se resume a duas coisas:

Consistência: se eu tiver ndelimitadores, em a, recebo os n+1valores de volta depois de split().

Deve ser possível fazer coisas complexas e fáceis de fazer coisas simples: se você quiser ignorar strings vazias como resultado de split(), você sempre pode fazer:

def mysplit(s, delim=None):
    return [x for x in s.split(delim) if x]

mas se não quisermos ignorar os valores vazios, devemos ser capazes.

A linguagem precisa escolher uma definição de split()- há muitos casos de uso diferentes para atender aos requisitos de todos como padrão. Acho que a escolha do Python é boa e a mais lógica. (À parte, uma das razões pelas quais eu não gosto do C strtok()é porque ele mescla delimitadores adjacentes, tornando extremamente difícil fazer análise / tokenização séria com ele.)

Há uma exceção: a.split()sem um argumento comprime espaços em branco consecutivos, mas pode-se argumentar que essa é a coisa certa a se fazer nesse caso. Se você não quiser o comportamento, você sempre pode a.split(' ').

Alok Singhal
fonte
Para aqueles que estão se perguntando se é mais rápido destruir espaços duplicados e, em seguida, dividir, ou dividir e apenas pegar strings não vazias, aqui está o que obtenho: python3 -m timeit "import re ; re.sub(' +', ' foo bar baz ', '').split(' ')"-> 875 nseg por loop; python3 -m timeit "[token for token in ' foo bar baz '.split(' ') if token]"-> 616 nseg por loop
s3cur3
8

Ter x.split(y)sempre retornado uma lista de 1 + x.count(y)itens é uma regularidade preciosa - como @ gnibbler já apontou, ele faz splite joinexatas inversos um do outro (como eles obviamente deveriam ser), ele também mapeia precisamente a semântica de todos os tipos de registros unidos por delimitadores ( como csvlinhas de arquivo [[net of Quoting Issues]], linhas de /etc/groupUnix, e assim por diante), permite (como a resposta de @ Roman mencionou) verificações fáceis para (por exemplo) caminhos absolutos vs relativos (em caminhos de arquivo e URLs), e assim por diante.

Outra maneira de ver isso é que você não deve jogar informações pela janela sem qualquer ganho. A que ganharíamos fazendo x.split(y)equivalente a x.strip(y).split(y)? Nada, é claro - é fácil de usar a segunda forma quando isso é o que você quer dizer, mas se a primeira forma foi arbitrariamente entender-se o segundo, você tem muito trabalho a fazer quando você não quer que o primeiro ( o que está longe de ser raro, como o parágrafo anterior indica).

Mas, na verdade, pensar em termos de regularidade matemática é a maneira mais simples e geral de aprender a projetar APIs passáveis. Para dar um exemplo diferente, é muito importante que, para qualquer xe y x == x[:y] + x[y:]- o que indica imediatamente por que um extremo de um corte deve ser excluído. Quanto mais simples a afirmação invariável que você pode formular, mais provável é que a semântica resultante seja o que você precisa nos usos da vida real - parte do fato místico de que a matemática é muito útil para lidar com o universo.

Tente formular o invariante para um splitdialeto no qual os delimitadores iniciais e finais são casados ​​especiais ... contra-exemplo: métodos de string como isspacenão são maximamente simples - x.isspace()é equivalente a x and all(c in string.whitespace for c in x)- aquele líder bobo x andé o motivo pelo qual você tantas vezes se encontra codificando not x or x.isspace(), para voltar à simplicidade que deveria ter sido projetada nos is...métodos de string (em que uma string vazia "é" qualquer coisa que você quiser - ao contrário do senso comum do homem da rua, talvez [[conjuntos vazios, como zero & c, sempre confundi a maioria das pessoas ;-)]], mas em total conformidade com o óbvio bom senso matemático refinado ! -).

Alex Martelli
fonte
5

Não tenho certeza de que tipo de resposta você está procurando? Você obtém três correspondências porque tem três delimitadores. Se você não quiser aquele vazio, basta usar:

'/segment/segment/'.strip('/').split('/')
Jamieb
fonte
4
-1 porque você obtém quatro combinações, não três, e também isso não responde realmente à pergunta.
Romano
1
+1 para neutralizar o neg .. Ele não disse que você obteria três resultados de volta. Ele disse "três combinações" para "três delimitadores", o que me parece lógico. No entanto, você não obtém "quatro combinações" de nada. No entanto, você obtém "quatro elementos" retornados em seu resultado. Além disso, não responde diretamente ao "porquê", mas fornece uma maneira simples de conseguir o que ele realmente quer ... o que eu não acho que mereça um downvote. Se você for criticar alguém (com um voto negativo, nada menos), tenha mais cuidado! Felicidades! 8 ^)
kodybrown
@wasatchwizard Obrigado pelo esclarecimento. Agradeço a correção e a recomendação. Infelizmente, agora meu voto está bloqueado e não pode ser alterado.
Romano
Adoro a sua solução - retire e depois divida para remover o resultado vazio
Nam G VU
5

Bem, permite que você saiba que havia um delimitador ali. Portanto, ver 4 resultados permite que você saiba que você tinha 3 delimitadores. Isso lhe dá o poder de fazer o que quiser com essas informações, em vez de fazer com que o Python elimine os elementos vazios e, em seguida, faça com que você verifique manualmente os delimitadores iniciais ou finais, se precisar saber.

Exemplo simples: digamos que você queira verificar nomes de arquivo absolutos vs. relativos. Desta forma, você pode fazer tudo com a divisão, sem ter que verificar também qual é o primeiro caractere do seu nome de arquivo.

romano
fonte
1

Considere este exemplo mínimo:

>>> '/'.split('/')
['', '']

splitdeve fornecer o que está antes e depois do delimitador '/', mas não há outros caracteres. Portanto, ele tem que fornecer a string vazia, que tecnicamente precede e segue o '/', porque '' + '/' + '' == '/'.

Timgeb
fonte