Eu apenas tive que escrever uma função de reversão de string em C # 2.0 (ou seja, LINQ não disponível) e surgiu com isso:
public string Reverse(string text)
{
char[] cArray = text.ToCharArray();
string reverse = String.Empty;
for (int i = cArray.Length - 1; i > -1; i--)
{
reverse += cArray[i];
}
return reverse;
}
Pessoalmente, não sou louco pela função e estou convencido de que há uma maneira melhor de fazê-la. Existe?
Respostas:
fonte
Aqui uma solução que reverte corretamente a string
"Les Mise\u0301rables"
como"selbare\u0301siM seL"
. Isso deve render comoselbarésiM seL
, nãoselbaŕesiM seL
(observe a posição do acento), como seria o resultado da maioria das implementações baseadas em unidades de código (Array.Reverse
, etc) ou mesmo em pontos de código (revertendo com cuidado especial para pares substitutos).(E exemplo de execução ao vivo aqui: https://ideone.com/DqAeMJ )
Ele simplesmente usa a API .NET para iteração de cluster grafema , que existe desde sempre, mas parece um pouco "escondido" da vista.
fonte
Esta está se tornando uma pergunta surpreendentemente complicada.
Eu recomendaria o uso do Array.Reverse na maioria dos casos, pois é codificado nativamente e é muito simples de manter e entender.
Parece ter um desempenho superior ao StringBuilder em todos os casos que testei.
Existe uma segunda abordagem que pode ser mais rápida para determinados comprimentos de string que usa Xor .
Nota Se você deseja suportar o conjunto de caracteres Unicode UTF16 completo, leia isto . E use a implementação lá. Ele pode ser otimizado ainda mais usando um dos algoritmos acima e executando a string para limpá-lo após os caracteres serem revertidos.
Aqui está uma comparação de desempenho entre o método StringBuilder, Array.Reverse e Xor.
Aqui estão os resultados:
Parece que o Xor pode ser mais rápido para seqüências curtas.
fonte
Se você pode usar o LINQ (.NET Framework 3.5+), seguir um liner fornecerá um código curto. Não se esqueça de adicionar
using System.Linq;
para ter acesso aEnumerable.Reverse
:Notas:
fonte
Se a sequência contiver dados Unicode (caracteres estritamente não-BMP), os outros métodos publicados a danificarão, porque você não poderá trocar a ordem das unidades de código substituto alto e baixo ao reverter a sequência. (Mais informações sobre isso podem ser encontradas no meu blog .)
O exemplo de código a seguir reverterá corretamente uma seqüência de caracteres que contém caracteres não-BMP, por exemplo, "\ U00010380 \ U00010381" (letra ugarítica Alpa, letra ugarítica Beta).
fonte
Ok, no interesse de "não se repita", ofereço a seguinte solução:
Meu entendimento é que essa implementação, disponível por padrão no VB.NET, lida corretamente com caracteres Unicode.
fonte
Greg Beech postou uma
unsafe
opção que é realmente o mais rápida possível (é uma reversão no local); mas, como ele indicou em sua resposta, é uma ideia completamente desastrosa .Dito isto, estou surpreso por haver tanto consenso que
Array.Reverse
é o método mais rápido. Ainda existe umaunsafe
abordagem que retorna uma cópia invertida de uma string (sem travessias de reversão no local) significativamente mais rápido que oArray.Reverse
método para strings pequenas:Aqui estão alguns resultados de benchmark .
Você pode ver que o ganho de desempenho diminui e desaparece no
Array.Reverse
método à medida que as seqüências de caracteres aumentam. Para cordas pequenas e médias, porém, é difícil vencer esse método.fonte
A resposta fácil e agradável é usar o método de extensão:
e aqui está a saída:
fonte
Reverse()
eToArray()
estão na ordem errada no seu exemplo de código.Se você quiser jogar um jogo realmente perigoso, essa é de longe a maneira mais rápida (cerca de quatro vezes mais rápida que o
Array.Reverse
método). É um reverso no local usando ponteiros.Observe que eu realmente não recomendo isso para qualquer uso ( veja aqui por alguns motivos pelos quais você não deve usar esse método ), mas é interessante ver que isso pode ser feito e que as strings não são realmente imutáveis depois de ativar o código não seguro.
fonte
unsafe
código que não é o mal e ainda bateArray.Reverse
em muitos casos. Dê uma olhada na minha resposta.Dê uma olhada na entrada da Wikipedia aqui . Eles implementam o método de extensão String.Reverse. Isso permite que você escreva um código como este:
Eles também usam a combinação ToCharArray / Reverse sugerida por outras respostas a esta pergunta. O código fonte fica assim:
fonte
Em primeiro lugar você não precisa ligar
ToCharArray
pois uma string já pode ser indexada como uma matriz de caracteres, portanto, isso poupará uma alocação.A próxima otimização é usar a
StringBuilder
para evitar alocações desnecessárias (como as strings são imutáveis, a concatenação faz uma cópia da string a cada vez). Para otimizar ainda mais isso, predefinimos o tamanho do arquivoStringBuilder
para que ele não precise expandir seu buffer.Editar: Dados de Desempenho
Testei essa função e a função usando
Array.Reverse
o seguinte programa simples, ondeReverse1
é uma função eReverse2
a outra:Acontece que para cordas curtas o
Array.Reverse
método é duas vezes mais rápido que o descrito acima, e para cordas mais longas a diferença é ainda mais acentuada. Portanto, dado que oArray.Reverse
método é mais simples e mais rápido, recomendo que você o use em vez deste. Deixo este aqui em cima apenas para mostrar que não é assim que você deve fazer (para minha surpresa!)fonte
Tente usar Array.Reverse
fonte
Claro que você pode estender a classe de string com o método Reverse
fonte
Enumerable.Reverse(input)
é igual ainput.Reverse()
"Melhor" pode depender de muitas coisas, mas aqui estão mais algumas alternativas curtas, ordenadas de rápida a lenta:
fonte
A partir do .NET Core 2.1, há uma nova maneira de reverter uma string usando o
string.Create
métodoObserve que esta solução não manipula caracteres combinados Unicode etc. corretamente, pois "Les Mise \ u0301rables" seria convertido em "selbarésiM seL". O outro responde por uma solução melhor.
Isso essencialmente copia os caracteres de
input
para uma nova sequência e reverte a nova sequência no local.Por que é
string.Create
útil?Quando criamos uma string a partir de uma matriz existente, uma nova matriz interna é alocada e os valores são copiados. Caso contrário, seria possível alterar uma sequência após sua criação (em um ambiente seguro). Ou seja, no snippet a seguir, temos que alocar uma matriz de comprimento 10 duas vezes, uma como buffer e outra como matriz interna da string.
string.Create
essencialmente nos permite manipular a matriz interna durante o tempo de criação da string. Ou seja, não precisamos mais de um buffer e, portanto, podemos evitar a alocação dessa matriz de caracteres.Steve Gordon escreveu sobre isso com mais detalhes aqui . Há também um artigo no MSDN .
Como usar
string.Create
?O método usa três parâmetros:
char
matriz interna da nova sequência e o segundo são os dados (estado) aos quais você passoustring.Create
.Dentro do delegado, podemos especificar como a nova string é criada a partir dos dados. No nosso caso, apenas copiamos os caracteres da string de entrada para os
Span
usados pela nova string. Então invertemos oSpan
e, portanto, toda a cadeia é invertida.Benchmarks
Para comparar minha maneira proposta de reverter uma string com a resposta aceita, escrevi dois benchmarks usando o BenchmarkDotNet.
Aqui estão os resultados na minha máquina:
Como você pode ver,
ReverseWithStringCreate
alocamos apenas metade da memória usada peloReverseWithArray
método.fonte
Não se preocupe com uma função, apenas faça-a no lugar. Nota: A segunda linha lançará uma exceção de argumento na janela Imediata de algumas versões do VS.
fonte
new string
Desculpe pela postagem longa, mas isso pode ser interessante
Resultados:
fonte
Resultado
Para tamanho: 10
Para tamanho: 100
Para o tamanho: 1000
Para tamanho: 10000
fonte
Reverse(...)
. Caso contrário, bom trabalho.Maneira mais simples:
fonte
Solução baseada em pilha.
Ou
fonte
Teve que enviar um exemplo recursivo:
fonte
E se:
fonte
ToCharArray
primeiro. O enumerador LINQ também é muito mais lento queArray.Reverse()
.Eu criei uma porta C # do Microsoft.VisualBasic.Strings . Não sei por que eles mantêm essas funções úteis (do VB) fora do System.String no Framework, mas ainda no Microsoft.VisualBasic. Mesmo cenário para funções financeiras (por exemplo
Microsoft.VisualBasic.Financial.Pmt()
).fonte
string s = "abo\u0327\u0307\u035d\U0001d166cd"
, que contém a letrao
seguida de 3 marcas diacríticas combinadas na BMP e uma marca combinada (MUSICAL SYMBOL COMBINING STEM) do plano astral (não BMP) e as mantém intactas. Mas o método é lento se esses caracteres aparecerem apenas no final de uma cadeia longa, pois ele deve passar duas vezes por toda a matriz.Desculpe por postar neste tópico antigo. Estou praticando algum código para uma entrevista.
Foi isso que eu criei para o C #. Minha primeira versão antes da refatoração foi horrível.
Em contraste com o
Array.Reverse
método abaixo, ele aparece mais rápido com 12 caracteres ou menos na string. Depois de 13 caracteres, oArray.Reverse
início começa a ficar mais rápido e, eventualmente, domina bastante a velocidade. Eu só queria indicar aproximadamente onde a velocidade começa a mudar.Com 100 caracteres na sequência, é mais rápido que minha versão x 4. No entanto, se eu soubesse que as seqüências sempre teriam menos de 13 caracteres, usaria a que criei.
O teste foi realizado com
Stopwatch
e 5000000 iterações. Além disso, não tenho certeza se minha versão lida com substitutos ou situações combinadas de caracteres comUnicode
codificação.fonte
O "caminho melhor" depende do que é mais importante para você na sua situação, desempenho, elegância, manutenção etc.
De qualquer forma, aqui está uma abordagem usando Array.Reverse:
fonte
Se alguma vez surgiu em uma entrevista e você foi informado de que não pode usar o Array.Reverse, acho que esse pode ser um dos mais rápidos. Ele não cria novas strings e itera apenas mais da metade da matriz (ou seja, iterações O (n / 2))
fonte
x
ou, no seu caso,n
não é usado. Seu algoritmo tem desempenhof(x) = x + ½x + C
, onde C é uma constante. Como ambosC
e o fator1½
não dependemx
, seu algoritmo éO(x)
. Isso não significa que não será mais rápido para nenhuma entrada de comprimentox
, mas seu desempenho depende linearmente do comprimento de entrada. Para responder a @MarcelValdezOrozco, sim, também éO(n)
, embora copie por pedaços de 16 bytes para melhorar a velocidade (não usa diretamentememcpy
o comprimento total).Se você tiver uma sequência que contenha apenas caracteres ASCII, poderá usar esse método.
fonte
Primeiro de tudo o que você precisa entender é que str + = redimensionará sua memória de strings para dar espaço a 1 caractere extra. Tudo bem, mas se você tem, digamos, um livro com 1000 páginas que deseja reverter, isso levará muito tempo para ser executado.
A solução que algumas pessoas podem sugerir está usando o StringBuilder. O que o construtor de strings faz quando você executa um + = é que ele aloca pedaços de memória muito maiores para reter o novo caractere, para que ele não precise realocar toda vez que adicionar um caractere.
Se você realmente deseja uma solução rápida e mínima, sugiro o seguinte:
Nesta solução, há uma alocação inicial de memória quando o char [] é inicializado e uma alocação quando o construtor de string cria a string a partir da matriz char.
No meu sistema, fiz um teste para você que inverte uma sequência de 2 750 000 caracteres. Aqui estão os resultados para 10 execuções:
StringBuilder: 190K - 200K ticks
Matriz de caracteres: 130K - 160K ticks
Também executei um teste para String normal + =, mas o abandonei após 10 minutos sem saída.
No entanto, também notei que, para cadeias menores, o StringBuilder é mais rápido; portanto, você terá que decidir sobre a implementação com base na entrada.
Felicidades
fonte
😀Les Misérables
fonte
fonte