Digamos que eu tenho o seguinte código:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", word1);
story = story.replace("bar", word2);
Após a execução desse código, o valor de story
será"Once upon a time, there was a foo and a foo."
Um problema semelhante ocorre se eu os substitui na ordem oposta:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("bar", word2);
story = story.replace("foo", word1);
O valor de story
será"Once upon a time, there was a bar and a bar."
Meu objetivo é transformar story
em "Once upon a time, there was a bar and a foo."
Como eu poderia fazer isso?
swap(String s1, String s2, String s3)
que troque todas as ocorrências des2
withs3
e vice-versa.Respostas:
Use o
replaceEach()
método do Apache Commons StringUtils :fonte
null
é aprovada, no entanto.Você usa um valor intermediário (que ainda não está presente na frase).
Como resposta às críticas: se você usar uma seqüência incomum grande o suficiente, como zq515sqdqs5d5sq1dqs4d1q5dqqé "& é5d4sqjshsjddjhodfqsqc, nvùq ^ µù; d & € sdq: d:;) àçlala e uso isso, é improvável que o ponto onde eu não vou nem discutir isso A única maneira de saber se o usuário o digitará é conhecendo o código-fonte e, nesse ponto, você estará com um outro nível de preocupação.
Sim, talvez haja maneiras regex sofisticadas. Eu prefiro algo legível que eu sei que também não vai acontecer comigo.
Reiterando também o excelente conselho dado por @David Conrad nos comentários :
fonte
Você pode tentar algo assim, usando
Matcher#appendReplacement
eMatcher#appendTail
:fonte
foo
,bar
estory
todos têm valores desconhecidos?"foo"
e"bar"
como o OP tinha em seu código, mas o mesmo tipo de abordagem funcionaria bem mesmo que esses valores não fossem conhecidos (você teria que usarif
/ emelse if
vez de umswitch
dentro dowhile
-ciclo).Pattern.quote
viria a calhar, ou\Q
e\E
.(foo)|(bar)
e depoism.group(1) != null
comparar, para evitar repetir as palavras correspondentes.Este não é um problema fácil. E quanto mais parâmetros de substituição de pesquisa você tiver, mais complicado será. Você tem várias opções, espalhadas na paleta de feio e elegante, eficiente e inútil:
Use
StringUtils.replaceEach
no Apache Commons como @AlanHay recomendado. Essa é uma boa opção se você estiver livre para adicionar novas dependências no seu projeto. Você pode ter sorte: a dependência já pode estar incluída no seu projetoUse um espaço reservado temporário como @Jeroen sugeriu e execute a substituição em 2 etapas:
Essa não é uma ótima abordagem, por várias razões: ela precisa garantir que as tags usadas na primeira etapa sejam realmente únicas; ele executa mais operações de substituição de cadeia do que o necessário
Crie uma regex a partir de todos os padrões e use o método com
Matcher
eStringBuffer
como sugerido por @arshajii . Isso não é terrível, mas também não é ótimo, já que a construção do regex é meio hackish, e envolve oStringBuffer
que saiu de moda há algum tempo a favor deStringBuilder
.Use uma solução recursiva proposta por @mjolka , dividindo a sequência nos padrões correspondentes e recorrendo nos segmentos restantes. Esta é uma solução fina, compacta e bastante elegante. Sua fraqueza são as potencialmente muitas operações de substring e concatenação e os limites de tamanho de pilha que se aplicam a todas as soluções recursivas
Divida o texto em palavras e use fluxos Java 8 para executar as substituições de maneira elegante, como sugerido pelo @msandiford , mas é claro que só funciona se você estiver de acordo com a divisão nos limites das palavras, o que o torna inadequado como solução geral
Aqui está minha versão, com base nas idéias emprestadas da implementação do Apache . Não é simples nem elegante, mas funciona e deve ser relativamente eficiente, sem etapas desnecessárias. Em poucas palavras, funciona da seguinte maneira: encontre repetidamente o próximo padrão de pesquisa correspondente no texto e use a
StringBuilder
para acumular os segmentos não correspondentes e as substituições.Testes unitários:
fonte
Procure a primeira palavra a ser substituída. Se estiver na sequência, recursione a parte da sequência antes da ocorrência e a parte da sequência após a ocorrência.
Caso contrário, continue com a próxima palavra a ser substituída.
Uma implementação ingênua pode se parecer com isso
Uso da amostra:
Resultado:
Uma versão menos ingênua:
Infelizmente, o Java
String
não temindexOf(String str, int fromIndex, int toIndex)
método. Omiti a implementaçãoindexOf
aqui, pois não tenho certeza de que esteja correta, mas ela pode ser encontrada no ideone , juntamente com alguns tempos aproximados de várias soluções postadas aqui.fonte
Uma linha no Java 8:
?<=
,?=
): http://www.regular-expressions.info/lookaround.htmlfonte
Aqui está uma possibilidade de fluxos do Java 8 que pode ser interessante para alguns:
Aqui está uma aproximação do mesmo algoritmo no Java 7:
fonte
Se você deseja substituir palavras em uma frase que são separadas por espaço em branco, como mostrado no seu exemplo, você pode usar este algoritmo simples.
Se a divisão no espaço não for aceitável, pode-se seguir este algoritmo alternativo. Você precisa usar a string mais longa primeiro. Se as cordas são bobas e bobas, você precisa primeiro usar bobas e depois bobinhas.
fonte
Aqui está uma resposta menos complicada usando o Mapa.
E o método é chamado
O resultado é: awesome is Raffy, Raffy Raffy is awesome awesome
fonte
replaced.replaceAll("Raffy", "Barney");
atrás disso tornará legen ... espere; Dary !!!Se você quiser lidar com várias ocorrências das cadeias de pesquisa a serem substituídas, faça isso facilmente dividindo a cadeia em cada termo de pesquisa e substituindo-a. Aqui está um exemplo:
fonte
Você pode atingir seu objetivo com o seguinte bloco de código:
Ele substitui as palavras independentemente da ordem. Você pode estender esse princípio para um método utilitário, como:
Que seria consumido como:
fonte
Isso funciona e é simples:
Você usa assim:
Nota: isso conta com Strings que não contêm caracteres
\ufdd0
, que é permanentemente reservado para uso interno pelo Unicode (consulte http://www.unicode.org/faq/private_use.html ):Não acho que seja necessário, mas se você quiser estar absolutamente seguro, poderá usar:
fonte
Troca de apenas uma ocorrência
Se houver apenas uma ocorrência de cada uma das seqüências de caracteres trocáveis na entrada, você poderá fazer o seguinte:
Antes de proceder a qualquer substituição, obtenha os índices das ocorrências das palavras. Depois disso, substituímos apenas a palavra encontrada nesses índices, e não todas as ocorrências. Esta solução usa
StringBuilder
e não produzString
s intermediáriosString.replace()
.Uma coisa a ser observada: se as palavras trocáveis tiverem comprimentos diferentes, após o primeiro substituir, o segundo índice poderá mudar (se a 1ª palavra ocorrer antes do 2º) exatamente pela diferença dos 2 comprimentos. Portanto, alinhar o segundo índice garantirá que isso funcione mesmo se estivermos trocando palavras com comprimentos diferentes.
Trocar número arbitrário de ocorrências
Analogamente ao caso anterior, coletaremos primeiro os índices (ocorrências) das palavras, mas, neste caso, será apresentada uma lista de números inteiros para cada palavra, não apenas um
int
. Para isso, usaremos o seguinte método utilitário:E, usando isso, substituiremos as palavras pelas outras, diminuindo o índice (que pode exigir a alternância entre as duas palavras trocáveis), para que nem precisemos corrigir os índices após a substituição:
fonte
indexOf
correspondente pode não ter o mesmo comprimento que a string de pesquisa, graças às idiossincrasias da equivalência de string unicode.String
é uma matriz de caracteres e não uma matriz de bytes. Todos os métodosString
eStringBuilder
operam com caracteres que não estão em bytes, que são "livres de codificação". Portanto, asindexOf
correspondências têm exatamente o mesmo comprimento (caractere) que as cadeias de pesquisa.ä
pode ser codificado como um único ponto de código ou como uma
seguido por uma combinação¨
. Também existem alguns pontos de código que são ignorados, como junções de largura zero (não). Não importa se a string consiste em bytes, caracteres ou qualquer outra coisa, mas quais regras de comparaçãoindexOf
usam. Ele pode usar simplesmente código-unidade por comparação de código-unidade ("Ordinal") ou pode implementar equivalência unicode. Eu não sei qual java escolheu."ab\u00ADc".IndexOf("bc")
retorna1
em .net correspondendo a cadeia de dois caracteresbc
a uma cadeia de três caracteres."ab\u00ADc".indexOf("bc")
retornos Java, o-1
que significa que"bc"
não foi encontrado em"ab\u00ADc"
. Portanto, ainda é válido que em Java o algoritmo acima funcione, asindexOf()
correspondências tenham exatamente o mesmo comprimento (caractere) que as cadeias de pesquisa e osindexOf()
relatórios somente correspondam se as charsequences (codepoints) corresponderem.É fácil escrever um método para fazer isso usando
String.regionMatches
:Teste:
Resultado:
Não é imediatamente óbvio, mas uma função como essa ainda pode depender da ordem em que as substituições são especificadas. Considerar:
Resultado:
Mas inverta as substituições:
Resultado:
Opa! :)
Portanto, às vezes é útil garantir a correspondência mais longa (como a
strtr
função do PHP faz, por exemplo). Esta versão do método fará isso:Observe que os métodos acima diferenciam maiúsculas de minúsculas. Se você precisar de uma versão que não diferencie maiúsculas de minúsculas, é fácil modificar as opções acima, pois
String.regionMatches
pode assumir umignoreCase
parâmetro.fonte
Se você não deseja nenhuma dependência, basta usar uma matriz que permita apenas uma alteração única. Esta não é a solução mais eficiente, mas deve funcionar.
Então, isso funcionaria.
fonte
Você está executando várias operações de substituição de pesquisa na entrada. Isso produzirá resultados indesejados quando as cadeias de substituição contiverem cadeias de pesquisa. Considere o exemplo foo-> bar, fo-bar, aqui estão os resultados para cada iteração:
Você precisa executar a substituição em uma iteração sem voltar. Uma solução de força bruta é a seguinte:
Uma função como
String.indexOfAny(String[]) -> int[]{index, whichString}
seria útil. Aqui está um exemplo (não o mais eficiente):Alguns testes:
Demo no IDEONE
Demo no IDEONE, código alternativo
fonte
Você sempre pode substituí-lo por uma palavra que, com certeza, não aparecerá em nenhum outro lugar na string e, em seguida, substituir o segundo mais tarde:
Observe que isso não funcionará corretamente se
"StringYouAreSureWillNeverOccur"
ocorrer.fonte
Considere usar StringBuilder
Em seguida, armazene o índice onde cada sequência deve começar. Se você usar um caractere de marcador de posição em cada posição, remova-o e insira a sequência de usuários. Você pode mapear a posição final adicionando o comprimento da string à posição inicial.
fonte
O que eu só posso compartilhar é o meu próprio método.
Você pode usar um temporário
String temp = "<?>";
ouString.Format();
Este é o meu exemplo de código criado no aplicativo de console via c # - "Apenas idéia, resposta não exata" .
Ou você também pode usar o
String.Format();
Resultado:
time upon a Once, there was a bar and a foo.
fonte
temp
from"_"
para<?>
. Mas, se necessário, o que ele pode fazer é adicionar outro parâmetro ao método que alterará a temperatura. - "é melhor manter as coisas simples, certo?"Aqui está a minha versão, que é baseada em palavras:
fonte
Maneira pouco complicada, mas você precisa fazer mais algumas verificações.
1.converter string em matriz de caracteres
2. loop no temp e substitua
foo
porbar
ebar
com,foo
pois não há chances de obter uma sequência substituível novamente.fonte
Bem, a resposta mais curta é ...
fonte
Usando a resposta encontrada aqui, você pode encontrar todas as ocorrências das strings que deseja substituir.
Por exemplo, você executa o código na resposta SO acima. Crie duas tabelas de índices (digamos que bar e foo não apareçam apenas uma vez na sua string) e você poderá trabalhar com essas tabelas para substituí-las na string.
Agora, para substituir em locais de índice específicos, você pode usar:
Considerando que
pos
é o índice onde suas strings iniciam (a partir das tabelas de índices citadas acima). Então, digamos que você criou duas tabelas de índices para cada uma. Vamos chamá-losindexBar
eindexFoo
.Agora, ao substituí-los, você pode simplesmente executar dois loops, um para cada substituição que deseja fazer.
Da mesma forma, outro loop para
indexFoo
.Isso pode não ser tão eficiente quanto as outras respostas aqui, mas é mais simples de entender do que o Maps ou outras coisas.
Isso sempre forneceria o resultado desejado e várias ocorrências possíveis de cada sequência. Contanto que você armazene o índice de cada ocorrência.
Além disso, esta resposta não precisa de recursão nem dependência externa. No que diz respeito à complexidade, provavelmente é O (n ao quadrado), enquanto n é a soma das ocorrências de ambas as palavras.
fonte
Eu desenvolvi este código vai resolver o problema:
No uso principal
change(story,word2,word1).
fonte
fonte