Eu tenho uma seqüência de caracteres multilinha que é delimitada por um conjunto de diferentes delimitadores:
(Text1)(DelimiterA)(Text2)(DelimiterC)(Text3)(DelimiterB)(Text4)
Posso dividir essa sequência em partes, usando String.split
, mas parece que não consigo obter a sequência real, que corresponde ao regex delimitador.
Em outras palavras, é isso que recebo:
Text1
Text2
Text3
Text4
É isso que eu quero
Text1
DelimiterA
Text2
DelimiterC
Text3
DelimiterB
Text4
Existe alguma maneira do JDK de dividir a cadeia usando um regex delimitador, mas também manter os delimitadores?
Respostas:
Você pode usar Lookahead e Lookbehind. Como isso:
E você receberá:
O último é o que você quer.
((?<=;)|(?=;))
é igual a para selecionar um caractere vazio antes;
ou depois;
.Espero que isto ajude.
Os comentários do EDIT Fabian Steeg sobre a legibilidade são válidos. A legibilidade é sempre o problema do RegEx. Uma coisa que faço para ajudar a facilitar isso é criar uma variável cujo nome represente o que a regex faz e use o formato Java String para ajudar nisso. Como isso:
Isso ajuda um pouco. :-D
fonte
split(";", true)
seria muito mais legível do quesplit("((?<=;)|(?=;))")
.String.format(WITH_DELIMITER, ";");
o formato é um método estático.[\\s,]+
) que você deseja corresponder completamente. As regexes necessárias ficam ainda mais longas, pois você precisa de uma visão negativa adicional {à frente, atrás} s para evitar combiná-las no meio, por exemplo.(?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+)
.Você deseja usar lookarounds e dividir em correspondências com largura zero. aqui estão alguns exemplos:
E sim, essa é uma afirmação triplamente aninhada no último padrão.
Perguntas relacionadas
Veja também
fonte
Uma solução muito ingênua, que não envolva regex, seria executar uma substituição de string no seu delimitador ao longo das linhas de (assumindo vírgula para delimitador):
Onde você pode substituir o tilda (~) por um delimitador exclusivo apropriado.
Então, se você fizer uma divisão no seu novo delimitador, acredito que obterá o resultado desejado.
fonte
Eu realmente não gosto do outro lado, onde você recebe um elemento vazio na frente e atrás. Um delimitador geralmente não está no início ou no final da string, portanto, na maioria das vezes, você acaba desperdiçando dois bons slots de array.
Edit: casos de limite fixo. A fonte comentada com casos de teste pode ser encontrada aqui: http://snippets.dzone.com/posts/show/6453
fonte
null
argumento é o caminho correto a seguir. A manipulação silenciosa leva a erros que aparecem mais tarde.Cheguei tarde, mas, voltando à pergunta original, por que não usar apenas lookarounds?
resultado:
Edição: O que você vê acima é o que aparece na linha de comando quando executo esse código, mas agora vejo que é um pouco confuso. É difícil acompanhar quais vírgulas fazem parte do resultado e quais foram adicionadas por
Arrays.toString()
. O destaque da sintaxe do SO também não está ajudando. Na esperança de obter o realce para o trabalho com me em vez de contra mim, aqui está como aquelas matrizes iria procurá-lo eu estávamos declarando-os em código fonte:Espero que seja mais fácil de ler. Obrigado pelo aviso, @finnw.
fonte
Sei que essa é uma pergunta muito antiga e a resposta também foi aceita. Ainda assim, gostaria de enviar uma resposta muito simples à pergunta original. Considere este código:
RESULTADO:
Estou apenas usando o limite da palavra
\b
para delimitar as palavras, exceto quando é o início do texto.fonte
abcdef
comde
como delimitador, mas você pode resolver o problema usando(?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de))
(?!^|$)
Dei uma olhada nas respostas acima e, honestamente, nenhuma delas me parece satisfatória. O que você quer fazer é imitar a funcionalidade de divisão do Perl. Por que o Java não permite isso e tem um método join () em algum lugar está além de mim, mas discordo. Você nem precisa de uma aula para isso realmente. É apenas uma função. Execute este programa de amostra:
Algumas das respostas anteriores têm verificação nula excessiva, que eu escrevi recentemente uma resposta para uma pergunta aqui:
https://stackoverflow.com/users/18393/cletus
Enfim, o código:
fonte
Eu gosto da idéia do StringTokenizer porque é enumerável.
Mas também é obsoleto e substitui por String.split, que retorna um String [] chato (e não inclui os delimitadores).
Então, eu implementei um StringTokenizerEx que é um Iterable e que requer um verdadeiro regexp para dividir uma string.
Um regexp verdadeiro significa que não é uma 'Sequência de caracteres' repetida para formar o delimitador:
'o' corresponderá apenas a 'o' e dividirá 'ooo' em três delimitadores, com duas cadeias vazias dentro:
Mas o regexp o + retornará o resultado esperado ao dividir "aooob"
Para usar este StringTokenizerEx:
O código desta classe está disponível nos DZone Snippets .
Como de costume, para uma resposta de desafio de código (uma classe independente com casos de teste incluídos), copie e cole-a (em um diretório 'src / test') e execute-a . Seu método main () ilustra os diferentes usos.
Nota: (edição de final de 2009)
O artigo Considerações finais: Java Puzzler: Splitting Hairs faz um bom trabalho explicando o comportamento bizarro de
String.split()
.Josh Bloch até comentou em resposta a esse artigo:
A biblioteca comum do Google Guava também contém um divisor que é:
Portanto, pode valer a pena conferir. A partir da documentação inicial inicial (pdf) :
fonte
Passe o terceiro aurgument como "true". Ele retornará delimitadores também.
fonte
Aqui está uma implementação simples e limpa, que é consistente
Pattern#split
e trabalha com padrões de comprimento variável, os quais, atrás, não podem suportar, e é mais fácil de usar. É semelhante à solução fornecida pelo @cletus.Eu não faço verificações nulas aqui,
Pattern#split
não, por que eu deveria. Eu não gostoif
do final, mas é necessário para consistência com oPattern#split
. Caso contrário, eu acrescentaria incondicionalmente, resultando em uma sequência vazia como o último elemento do resultado se a sequência de entrada terminar com o padrão.Eu converter para String [] para consistência com
Pattern#split
, eu uso emnew String[0]
vez denew String[result.size()]
, veja aqui porquê.Aqui estão os meus testes:
fonte
Vou postar minhas versões de trabalho também (primeiro é realmente semelhante ao Markus).
E aqui está a segunda solução e sua rodada 50% mais rápida que a primeira:
fonte
Outra solução candidata usando uma regex. Mantém a ordem do token, corresponde corretamente a vários tokens do mesmo tipo em uma linha. A desvantagem é que o regex é meio desagradável.
Saída de amostra:
fonte
Não conheço uma função existente na API Java que faça isso (o que não quer dizer que não exista), mas aqui está minha própria implementação (um ou mais delimitadores serão retornados como um único token; se você quiser cada delimitador a ser retornado como um token separado, será necessário um pouco de adaptação):
fonte
Sugiro usar Pattern and Matcher, que quase certamente alcançará o que você deseja. Sua expressão regular precisará ser um pouco mais complicada do que o que você está usando no String.split.
fonte
Eu não acho que é possível com
String#split
, mas você pode usar aStringTokenizer
, embora isso não permita que você defina seu delimitador como uma expressão regular, mas apenas como uma classe de caracteres de um dígito:fonte
Se você puder pagar, use o método replace (destino de CharSequence, substituição de CharSequence) do Java e preencha outro delimitador para dividir. Exemplo: eu quero dividir a string "boo: and: foo" e manter ':' na string direita.
Nota importante: Isso só funciona se você não tiver mais "novo delimitador" na sua String! Portanto, não é uma solução geral. Mas se você conhece um CharSequence do qual pode ter certeza de que ele nunca aparecerá na String, esta é uma solução muito simples.
fonte
Resposta rápida: use limites não físicos como \ b para dividir. Vou tentar experimentar para ver se funciona (usado no PHP e JS).
É possível, e tipo de trabalho, mas pode dividir demais. Na verdade, isso depende da string que você deseja dividir e do resultado que você precisa. Dê mais detalhes, nós o ajudaremos melhor.
Outra maneira é fazer sua própria divisão, capturando o delimitador (supondo que seja variável) e adicionando-o posteriormente ao resultado.
Meu teste rápido:
Resultado:
Um pouco demais ... :-)
fonte
Tweaked Pattern.split () para incluir o padrão correspondente na lista
Adicionado
Fonte completa
fonte
Aqui está uma versão interessante com base em alguns dos códigos acima, caso isso ajude. É curto, pelo menos. Inclui condicionalmente a cabeça e a cauda (se não estiverem vazias). A última parte é um caso de demonstração / teste.
fonte
Uma solução extremamente ingênua e ineficiente que funciona, no entanto. Use dividir duas vezes na string e concatenar as duas matrizes
fonte
fonte
Scanner scanner = new Scanner("((A+B)*C-D)*E"); scanner.useDelimiter("((?<=[\\+\\*\\-\\/\\(\\)])|(?=[\\+\\*\\-\\/\\(\\)]))"); while (scanner.hasNext()) { System.out.print(" " + scanner.next()); }
Uma das sutilezas desta pergunta envolve a pergunta "delimitador principal": se você deseja ter uma matriz combinada de tokens e delimitadores, precisa saber se ele começa com um token ou um delimitador. Obviamente, você pode simplesmente assumir que um delim principal deve ser descartado, mas isso parece uma suposição injustificada. Você também pode querer saber se possui ou não um delim à direita. Isso define dois sinalizadores booleanos de acordo.
Escrito em Groovy, mas uma versão Java deve ser bastante óbvia:
fonte
Eu não conheço Java muito bem, mas se você não conseguir encontrar um método Split que faça isso, sugiro que você faça o seu próprio.
Não é muito elegante, mas serve.
fonte