Por que “dividir” em uma string vazia retorna um array não vazio?

111

A divisão em uma string vazia retorna uma matriz de tamanho 1:

scala> "".split(',')
res1: Array[String] = Array("")

Considere que isso retorna uma matriz vazia:

scala> ",,,,".split(',')
res2: Array[String] = Array()

Por favor explique :)

oluies
fonte
5
Além disso, parece inconsistente com o comportamento observado quando a string contém apenas uma instância do separador. Neste caso, o resultado é efetivamente um array vazio: ",". Split (","). Length == 0
LD.

Respostas:

37

Pela mesma razão que

",test" split ','

e

",test," split ','

retornará uma matriz de tamanho 2. Tudo antes da primeira correspondência é retornado como o primeiro elemento.

Daniel C. Sobral
fonte
5
String vazia é uma string, não nada. (em qualquer lugar, menos no Excel)
Raphael
5
@Raphael Ou em um banco de dados Oracle
Austin
7
@Raphael, em qualquer outra linguagem de programação "".split("wtf").lengthretorna 0. Somente em JS é 1.: /
Andrey Mikhaylov - lolmaus
11
@ DanielC.Sobral Ok, então por que "," split ","retorna um array de 0?
Joan
5
Por que não voltou tudo depois da última partida?
Didier A.
72

Se você dividir uma laranja zero vezes, terá exatamente um pedaço - a laranja.

Sam Stainsby
fonte
8
Mas a laranja não está vazia (idk se é isso que oluies significava), é uma laranja. Talvez dividir uma laranja que deveria estar lá, mas não está, então você recebe de volta um único valor: um espaço vazio xD
Nick Rolando
8
Esta é uma conversa profunda.
31
Essa metáfora faz sentido "orange".split(','), mas não é obviamente relevante para dividir strings vazias. Se eu dividir minha falta de laranja zero vezes, ainda não tenho laranja; representamos isso como uma lista vazia de sem laranja, uma lista de exatamente uma sem laranja, uma lista de doze sem laranja, ou o quê? Não é uma questão de com o que acabamos, mas como o representamos.
Matchu
1
Mas se você dividir um livro inexistente pelas páginas, não obterá nada.
SMUsamaShah
49

Os métodos de divisão Java e Scala operam em duas etapas como esta:

  • Primeiro, divida a string por delimitador. A consequência natural é que, se a string não contém o delimitador, um array de singleton contendo apenas a string de entrada é retornado,
  • Em segundo lugar, remova todas as strings vazias mais à direita. Este é o motivo pelo qual ",,,".split(",")retorna um array vazio.

De acordo com isso, o resultado de "".split(",")deve ser um array vazio por causa da segunda etapa, certo?

Deveria. Infelizmente, este é um caso de canto introduzido artificialmente. E isso é ruim, mas pelo menos ele está documentado em java.util.regex.Pattern, se você se lembrar de tomar uma olhada na documentação:

Para n == 0, o resultado é igual a n <0, exceto que as strings vazias finais não serão retornadas. (Observe que o caso em que a própria entrada é uma string vazia é especial, conforme descrito acima, e o parâmetro limit não se aplica lá.)

Solução 1: sempre passe -1 como o segundo parâmetro

Portanto, aconselho você a sempre passar n == -1como o segundo parâmetro (isso pulará a etapa dois acima), a menos que você saiba especificamente o que deseja alcançar / tenha certeza de que a string vazia não é algo que seu programa obteria como entrada.

Solução 2: usar a classe Guava Splitter

Se você já está usando Guava em seu projeto, você pode tentar a classe Divisor (documentação) . Tem uma API muito rica e torna o seu código muito fácil de entender.

Splitter.on(".").split(".a.b.c.") // "", "a", "b", "c", ""
Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c"
Splitter.on(CharMatcher.anyOf(",.")).split("a,b.c") // "a", "b", "c"
Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c"
Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c"
Rok Kralj
fonte
1
+1, esta é a única resposta que realmente cita a documentação e aponta que ela é inconsistente. No entanto, não encontrei a parte destacada do comentário em meu JavaDoc.
Yogu
Encontrei-o em java.util.regex.Pattern, mas parece que quase não existe mais. No momento da escrita, ele definitivamente estava presente na árvore de código-fonte oficial do OpenJDK como um javadoc. android.googlesource.com/platform/libcore/+/… Talvez devêssemos relatar um bug?
Rok Kralj
Seria uma boa ideia relatar um bug - o comportamento definitivamente não será alterado, mas deve pelo menos ser documentado.
Yogu
@RokKralj Android não usava a biblioteca OpenJDK, mas era baseado no Apache Harmony, então talvez você esteja procurando no lugar errado?
lxgr
1
"".split (",", n)gera uma matriz de um elemento para n em (-1, 0, 1) com Oracle JDK 8. Seria bom obter uma lista de tokens não vazios apenas - acho que um regex completo pode ser necessário (algo como "[^,\\s]+[^,]*[^,\\s]*").
simon.watts
40

A divisão de uma string vazia retorna a string vazia como o primeiro elemento. Se nenhum delimitador for encontrado na string de destino, você obterá uma matriz de tamanho 1 que contém a string original, mesmo se estiver vazia.

Nick Rolando
fonte
2
Errado. A divisão remove todas as strings vazias mais à direita, portanto, o resultado deve ser um array vazio. Veja minha resposta. ",".split(",")retorna uma matriz vazia.
Rok Kralj de
23

"a".split(",")-> "a" portanto "".split(",")->""

weberjn
fonte
6
Errado. A divisão remove todas as strings vazias mais à direita, portanto, o resultado deve ser um array vazio. Veja minha resposta. ",".split(",")retorna uma matriz vazia.
Rok Kralj de
5

Em todas as linguagens de programação, sei que uma string em branco ainda é uma String válida. Portanto, fazer uma divisão usando qualquer delimitador sempre retornará uma única matriz de elemento em que esse elemento é a String em branco. Se fosse uma string nula (não em branco), o problema seria diferente.

brent777
fonte
Acho que é uma função de biblioteca e não uma parte da linguagem. Por exemplo, no google goiaba você pode omitir strings vazias. > Iterable <String> pieces = com.google.common.base.Splitter.on (','). OmitEmptyStrings (). Split ("");
oluies
2

Este splitcomportamento é herdado do Java, para melhor ou para pior ...
Scala não sobrescreve a definição do Stringprimitivo.

Observe que você pode usar o limitargumento para modificar o comportamento :

O parâmetro limit controla o número de vezes que o padrão é aplicado e, portanto, afeta o comprimento da matriz resultante. Se o limite n for maior que zero, o padrão será aplicado no máximo n - 1 vezes, o comprimento da matriz não será maior do que n e a última entrada da matriz conterá todas as entradas além do último delimitador correspondente. Se n for não positivo, o padrão será aplicado tantas vezes quanto possível e a matriz pode ter qualquer comprimento. Se n for zero, o padrão será aplicado tantas vezes quanto possível, a matriz pode ter qualquer comprimento e as cadeias de caracteres vazias posteriores serão descartadas.

ou seja, você pode definir o limit=-1para obter o comportamento de (todos?) outros idiomas:

@ ",a,,b,,".split(",")
res1: Array[String] = Array("", "a", "", "b")

@ ",a,,b,,".split(",", -1)  // limit=-1
res2: Array[String] = Array("", "a", "", "b", "", "")

Parece ser bem conhecido que o comportamento do Java é bastante confuso, mas:

O comportamento acima pode ser observado de pelo menos Java 5 a Java 8.

Houve uma tentativa de alterar o comportamento para retornar um array vazio ao dividir uma string vazia em JDK-6559590 . No entanto, logo foi revertido em JDK-8028321 quando causa regressão em vários lugares. A mudança nunca chega ao lançamento inicial do Java 8.

Nota: O método de divisão não estava em Java desde o início (não está em 1.0.2 ), mas na verdade existe a partir de pelo menos 1.4 (por exemplo, consulte JSR51 por volta de 2002). Ainda estou investigando ...

O que não está claro é por que Java escolheu isso em primeiro lugar (minha suspeita é que era originalmente um descuido / bug em um "caso extremo"), mas agora irrevogavelmente embutido na linguagem e assim permanece .

Andy Hayden
fonte
Não tenho certeza se isso responde à pergunta - embora possa ser verdade para o exemplo dado aqui, não ajuda com o caso da string vazia - "".split(",")ainda retorna uma única matriz de elemento como [""].
DaveyDaveDave
@DaveyDaveDave é o comportamento esperado de qualquer outra linguagem. O ",,,," é o comportamento bizarro / diferente no Scala, e diferente do caso "".
Andy Hayden,
0

A string vazia não tem nenhum status especial ao dividir uma string. Você pode usar:

Some(str)
  .filter(_ != "")
  .map(_.split(","))
  .getOrElse(Array())
Hanan Oanunu
fonte