Por que “dividir” em uma string vazia retorna uma matriz não vazia?

Dividir em uma string vazia retorna uma matriz de tamanho 1:

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

Considere que isso retorna um array vazio:

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

Por favor explique 🙂

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.

Se você dividir um zero laranja, você terá exatamente um pedaço – o laranja.

Dividir uma string vazia retorna a string vazia como o primeiro elemento. Se nenhum delimitador for encontrado na cadeia de destino, você obterá uma matriz de tamanho 1 que mantém a cadeia original, mesmo que esteja vazia.

Os methods de divisão Java e Scala operam em duas etapas como esta:

  • Primeiro, divida a string por delimitador. A conseqüência natural é que, se a string não contiver o delimitador, um array singleton contendo apenas a string de input será retornado,
  • Em segundo lugar, remova todas as strings vazias mais à direita. Esta é a razão ",,,".split(",") retorna array vazio.

De acordo com isso, o resultado de "".split(",") deve ser um array vazio devido ao segundo passo, certo?

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

Para n == 0, o resultado é como para n <0, exceto que strings vazias à direita não serão retornadas. (Observe que o caso em que a input é em si uma string vazia é especial, conforme descrito acima, e o parâmetro de limite não se aplica lá.)

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

Então, eu aconselho você a passar sempre n == -1 como o segundo parâmetro (isto irá pular o passo dois acima), a menos que você saiba especificamente o que você quer alcançar / você tem certeza que a string vazia não é algo que seu programa faria obter como uma input.

TL; DR: A divisão da string vazia é um caso de canto introduzido artificialmente e a documentação avisa sobre isso. Sempre passe -1 como o segundo parâmetro para evitar erros, a menos que você tenha um bom motivo.

Solução 2: Use a class Guava Splitter

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

 Splitter.on(".").split(".abc") // "", "a", "b", "c", "" Splitter.on(",").omitEmptyStrings().split("a,,b,,c") // "a", "b", "c" Splitter.on(CharMatcher.anyOf(",.")).split("a,bc") // "a", "b", "c" Splitter.onPattern("=>?").split("a=b=>c") // "a", "b", "c" Splitter.on(",").limit(2).split("a,b,c") // "a", "b,c" 

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

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 elementos, em que esse elemento é a String em branco. Se fosse uma string nula (não em branco), isso seria um problema diferente.

Esse comportamento de split é herdado do Java, para melhor ou pior …
O Scala não substitui a definição do primitivo String .

Note que você pode usar o argumento limit para modificar o comportamento :

O parâmetro limite 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 que n ea última input da matriz conterá todas as inputs além do último delimitador correspondido. Se n não for positivo, o padrão será aplicado quantas vezes for possível e o array poderá ter qualquer tamanho. Se n for zero, o padrão será aplicado quantas vezes for possível, a matriz poderá ter qualquer comprimento e as sequências vazias finais serão descartadas.

ou seja, você pode definir o limit=-1 para 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 de Java é bastante confuso, mas:

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

Houve uma tentativa de alterar o comportamento para retornar uma matriz vazia ao dividir uma cadeia vazia no JDK-6559590 . No entanto, logo foi revertido no JDK-8028321 quando causa regressão em vários lugares. A mudança nunca chega à versão inicial do Java 8.

Nota: O método split não estava em Java desde o início (não está em 1.0.2 ), mas na verdade está lá pelo menos em 1.4 (por exemplo, veja JSR51 por volta de 2002). Eu ainda estou investigando …

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