Como substituir parte da corda pela posição?

155

Eu tenho esta string: ABCDEFGHIJ

Preciso substituir da posição 4 para a posição 5 pela corda ZX

Isso parecerá assim: ABCZXFGHIJ

Mas não para usar com string.replace("DE","ZX")- eu preciso usar com posição

Como eu posso fazer isso?

Gali
fonte
@TotZam - verifique as datas. Este é mais antigo que o que você vinculou.
Página
@ToolmakerSteve Costumo olhar para a qualidade das perguntas e respostas, não das datas, como dito aqui . Nesse caso, parece que cometi um erro e cliquei no errado para marcar como duplicado, pois a qualidade dessa pergunta é obviamente melhor e, portanto, sinalizei a outra pergunta.
Tot Zam
@ TotZam - ah, eu não sabia sobre essa recomendação - obrigado por apontá-la. (Embora seja confuso ver algo mais antigo relatado como duplicado de algo mais novo, nesse caso, vale a pena explicar explicitamente que você está marcando como duplicada uma pergunta MAIS ANTIGA, porque a pergunta vinculada tem respostas melhores.)
ToolmakerSteve

Respostas:

198

A maneira mais fácil de adicionar e remover intervalos em uma string é usar o StringBuilder.

var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();

Uma alternativa é usar String.Substring, mas acho que o StringBuildercódigo fica mais legível.

Albin Sunnanbo
fonte
6
seria melhor enrolamento que se em um método de extensão :)
balexandre
Por que você usa um StringBuilder aqui? Verifique a resposta de V4Vendetta, que é muito mais legível ...
Fortega
1
@ Fortega Remove () e Insert () criam e retornam uma nova string. A abordagem StringBuilder evita esse custo extra trabalhando em uma seção pré-alocada da memória e movendo os caracteres dentro dela.
Redcalx #
@redcalx Isso é verdade, mas um custo extra é introduzido através da introdução de um objeto StringBuilder e reduzindo a legibilidade
Fortega
2
Qual é o sentido de usar o StringBuilder aqui, se você estiver chamando Remover e Inserir da mesma maneira que V4Vendetta, mas ele está fazendo isso diretamente na cadeia? Parece código redundante.
metabuddy
186
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
V4Vendetta
fonte
17
Eu prefiro isso do que inicializar um novo StringBuilder (...). Obrigado!
Michael Norgren
3
Não sei por que alguém usaria stringbuilder para esta operação. Esta é uma boa resposta #
NappingRabbit 16/07/19
43

ReplaceAt (índice int, comprimento int, substituição de cadeia)

Aqui está um método de extensão que não usa StringBuilder ou Substring. Esse método também permite que a cadeia de substituição se estenda além do comprimento da cadeia de origem.

//// str - the source string
//// index- the start location to replace at (0-based)
//// length - the number of characters to be removed before inserting
//// replace - the string that is replacing characters
public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return str.Remove(index, Math.Min(length, str.Length - index))
            .Insert(index, replace);
}

Ao usar esta função, se você desejar que toda a cadeia de substituição substitua o máximo de caracteres possível, defina o comprimento como o comprimento da cadeia de substituição:

"0123456789".ReplaceAt(7, 5, "Hello") = "0123456Hello"

Caso contrário, você pode especificar a quantidade de caracteres que serão removidos:

"0123456789".ReplaceAt(2, 2, "Hello") = "01Hello456789"

Se você especificar o comprimento como 0, essa função funcionará exatamente como a função de inserção:

"0123456789".ReplaceAt(4, 0, "Hello") = "0123Hello456789"

Eu acho que isso é mais eficiente, uma vez que a classe StringBuilder não precisa ser inicializada e uma vez que usa operações mais básicas. Por favor corrija-me se eu estiver errado. :)

Nicholas Miller
fonte
5
O construtor de strings é mais eficiente se o string for maior que, digamos, 200 caracteres.
Tim Schmelter
Sem estresse. Trabalhou em linha reta. Obrigado
D_Edet
9

Use String.Substring()(detalhes aqui ) para cortar a parte esquerda, a sua substituição e a parte direita. Brinque com índices até acertar :)

Algo como:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);
Daniel Mošmondor
fonte
2
Eu criei três métodos que usam os três métodos acima (string.Remove / Inserir, Stringbuilder.Remove / Inserir e resposta Substring de Daniel, e SubString foi o método mais rápido.
Francine DeGrood Taylor
7

Como um método de extensão.

public static class StringBuilderExtension
{
    public static string SubsituteString(this string OriginalStr, int index, int length, string SubsituteStr)
    {
        return new StringBuilder(OriginalStr).Remove(index, length).Insert(index, SubsituteStr).ToString();
    }
}
Ali Khalid
fonte
5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);
Javed Akram
fonte
Isso funciona, você provavelmente não quer recolocar t.
Pierre
4

Se você se preocupa com o desempenho, o que você deseja evitar aqui são alocações. E se você estiver no .Net Core 2.1+ (ou ainda não lançado, .Net Standard 2.1), poderá usar o string.Createmétodo :

public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return string.Create(str.Length - length + replace.Length, (str, index, length, replace),
        (span, state) =>
        {
            state.str.AsSpan().Slice(0, state.index).CopyTo(span);
            state.replace.AsSpan().CopyTo(span.Slice(state.index));
            state.str.AsSpan().Slice(state.index + state.length).CopyTo(span.Slice(state.index + state.replace.Length));
        });
}

Essa abordagem é mais difícil de entender do que as alternativas, mas é a única que alocará apenas um objeto por chamada: a sequência recém-criada.

svick
fonte
3

Você pode tentar algo vincular isso:

string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);
MBU
fonte
3

Como outros já mencionaram, a Substring()função existe por uma razão:

static void Main(string[] args)
{
    string input = "ABCDEFGHIJ";

    string output = input.Overwrite(3, "ZX"); // 4th position has index 3
    // ABCZXFGHIJ
}

public static string Overwrite(this string text, int position, string new_text)
{
    return text.Substring(0, position) + new_text + text.Substring(position + new_text.Length);
}

Também cronometrei isso contra a StringBuildersolução e obtive 900 tiques vs. 875. Portanto, é um pouco mais lento.

John Alexiou
fonte
@ Aaron.S - não, não. No meu exemplo "ABCDEFGHIJ"e "ZX"são de diferentes comprimentos.
John Alexiou
3

Ainda outra

    public static string ReplaceAtPosition(this string self, int position, string newValue)        
    {
        return self.Remove(position, newValue.Length).Insert(position, newValue); 
    }
Stijn Van Antwerpen
fonte
2

Com a ajuda deste post, crio a seguinte função com verificações adicionais de comprimento

public string ReplaceStringByIndex(string original, string replaceWith, int replaceIndex)
{
    if (original.Length >= (replaceIndex + replaceWith.Length))
    {
        StringBuilder rev = new StringBuilder(original);
        rev.Remove(replaceIndex, replaceWith.Length);
        rev.Insert(replaceIndex, replaceWith);
        return rev.ToString();
    }
    else
    {
        throw new Exception("Wrong lengths for the operation");
    }
}
Hassan
fonte
2

Todas as outras respostas não funcionam se a string contiver um caractere Unicode (como Emojis), porque um caractere Unicode pesa mais bytes que um caractere.

Exemplo : o emoji '🎶' convertido em bytes pesará o equivalente a 2 caracteres. Portanto, se o caractere unicode for colocado no início da sua string, o offsetparâmetro será alterado).

Com este tópico , estendo a classe StringInfo para Replace by position mantendo o algoritmo de Nick Miller para evitar que:

public static class StringInfoUtils
{
    public static string ReplaceByPosition(this string str, string replaceBy, int offset, int count)
    {
        return new StringInfo(str).ReplaceByPosition(replaceBy, offset, count).String;
    }

    public static StringInfo ReplaceByPosition(this StringInfo str, string replaceBy, int offset, int count)
    {
        return str.RemoveByTextElements(offset, count).InsertByTextElements(offset, replaceBy);
    }

    public static StringInfo RemoveByTextElements(this StringInfo str, int offset, int count)
    {
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            offset + count < str.LengthInTextElements
                ? str.SubstringByTextElements(offset + count, str.LengthInTextElements - count - offset)
                : ""
            ));
    }
    public static StringInfo InsertByTextElements(this StringInfo str, int offset, string insertStr)
    {
        if (string.IsNullOrEmpty(str?.String))
            return new StringInfo(insertStr);
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            insertStr,
            str.LengthInTextElements - offset > 0 ? str.SubstringByTextElements(offset, str.LengthInTextElements - offset) : ""
        ));
    }
}
GGO
fonte
dotnetfiddle.net/l0dqS5 Não foi possível obter outros métodos para falhar no Unicode. Altere o violino para demonstrar seu caso de uso. Muito interessado, em resultados.
Markus
1
@MarkusHooge Tente colocar os caracteres unicode no início da sua string, como: dotnetfiddle.net/3V2K3Y . Você verá que apenas a última linha funciona bem e coloca o caractere no 4º lugar (no outro caso, o caractere unicode leva 2 caracteres de comprimento)
GGO
Você está certo, não está claro. Atualizei minha resposta para adicionar mais explicações sobre por que as outras respostas não funcionam
GGO 15/03/19
1

Eu estava procurando uma solução com os seguintes requisitos:

  1. use apenas uma única expressão de uma linha
  2. use apenas métodos internos do sistema (nenhum utilitário implementado personalizado)

Solução 1

A solução que melhor me convém é esta:

// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();

Isso usa StringBuilder.Replace(oldChar, newChar, position, count)

Solução 2

A outra solução que atende aos meus requisitos é usar Substringcom concatenação:

string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);

Tudo bem também. Eu acho que não é tão eficiente quanto o primeiro em termos de desempenho (devido a concatenação desnecessária de strings). Mas a otimização prematura é a raiz de todo mal .

Então escolha o que você mais gosta :)

KFL
fonte
A solução 2 funciona, mas requer correção: string newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
Yura G
0

É melhor usar o String.substr().

Como isso:

ReplString = GivenStr.substr(0, PostostarRelStr)
           + GivenStr(PostostarRelStr, ReplString.lenght());
Varun Kumar
fonte
0
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();

Deixe-me explicar minha solução.
Dada a declaração do problema de alterar uma sequência em suas duas posições específicas (“posição 4 para a posição 5”) com dois caracteres 'Z' e 'X', a solicitação é usar o índice de posição para alterar a sequência e não a sequência Substituir ( ) (pode ser devido à possibilidade de repetição de alguns caracteres na string real), eu preferiria usar uma abordagem minimalista para atingir o objetivo em vez de usar uma Substring()string Concat()ou string Remove()e Insert()abordagem. Embora todas essas soluções tenham o objetivo de atingir o mesmo objetivo, isso depende apenas da escolha pessoal e da filosofia de se estabelecer com uma abordagem minimalista.
Voltando à minha solução mencionada acima, se dermos uma olhada mais de perto stringeStringBuilder, ambos tratam internamente uma determinada sequência como uma matriz de caracteres. Se olharmos para a implementação, StringBuilderela mantém uma variável interna algo como “ internal char[] m_ChunkChars;” para capturar a sequência especificada. Agora, como essa é uma variável interna, não podemos acessar diretamente a mesma. Para o mundo externo, para poder acessar e alterar essa matriz de caracteres, os StringBuilderexpõe através da propriedade do indexador, que se parece com abaixo

    [IndexerName("Chars")]
    public char this[int index]
    {
      get
      {
        StringBuilder stringBuilder = this;
        do
        {
          // … some code
            return stringBuilder.m_ChunkChars[index1];
          // … some more code
        }
      }
      set
      {
        StringBuilder stringBuilder = this;
        do
        {
            //… some code
            stringBuilder.m_ChunkChars[index1] = value;
            return;
            // …. Some more code
        }
      }
    }

Minha solução mencionada acima utiliza esse recurso do indexador para alterar diretamente a matriz de caracteres mantida internamente, que o IMO é eficiente e minimalista.

BTW; podemos reescrever a solução acima de forma mais elaborada, algo como abaixo

 string myString = "ABCDEFGHIJ";
 StringBuilder tempString = new StringBuilder(myString);
 tempString[3] = 'Z';
 tempString[4] = 'X';
 string modifiedString = tempString.ToString();

Nesse contexto, também gostaria de mencionar que, no caso string, também possui propriedade indexadora como forma de expor sua matriz de caracteres internos, mas nesse caso, ela possui apenas a propriedade Getter (e nenhum Setter), pois a string é imutável por natureza. E é por isso que precisamos usar StringBuilderpara alterar a matriz de caracteres.

[IndexerName("Chars")]
public extern char this[int index] { [SecuritySafeCritical, __DynamicallyInvokable, MethodImpl(MethodImplOptions.InternalCall)] get; }

E por último, mas não menos importante, esta solução é a mais adequada para esse problema específico, em que a solicitação é substituir apenas alguns caracteres por um índice de posição conhecido antecipadamente. Pode não ser o melhor ajuste quando o requisito é alterar uma sequência bastante longa, ou seja, o número de caracteres a serem alterados é grande em números.

Até parece
fonte
1
Por favor edite sua resposta para explicar como este código responde à pergunta
tshimkus
0
String timestamp = "2019-09-18 21.42.05.000705";
String sub1 = timestamp.substring(0, 19).replace('.', ':'); 
String sub2 = timestamp.substring(19, timestamp.length());
System.out.println("Original String "+ timestamp);      
System.out.println("Replaced Value "+ sub1+sub2);
user1355816
fonte
0

Aqui está um método de extensão simples:

    public static class StringBuilderExtensions
    {
        public static StringBuilder Replace(this StringBuilder sb, int position, string newString)
            => sb.Replace(position, newString.Length, newString);

        public static StringBuilder Replace(this StringBuilder sb, int position, int length, string newString)
            => (newString.Length <= length)
                ? sb.Remove(position, newString.Length).Insert(position, newString)
                : sb.Remove(position, length).Insert(position, newString.Substring(0, length));
    }

Use-o assim:

var theString = new string(' ', 10);
var sb = new StringBuilder(theString);
sb.Replace(5, "foo");
return sb.ToString();
MovGP0
fonte
-2

Eu acredito que a maneira mais simples seria essa: (sem construtor de string)

string myString = "ABCDEFGHIJ";
char[] replacementChars = {'Z', 'X'};
byte j = 0;

for (byte i = 3; i <= 4; i++, j++)  
{                   
myString = myString.Replace(myString[i], replacementChars[j]);  
}

Isso funciona porque uma variável do tipo string pode ser tratada como uma matriz de variáveis ​​de caracteres. Você pode, por exemplo, consultar o segundo caractere de uma variável de cadeia com o nome "myString" como myString [1]

Sfou
fonte
Isso funciona apenas porque no exemplo do autor stringos caracteres a serem substituídos ocorrem apenas uma vez. Se você executar isso contra, digamos, "DBCDEFGHIE"obterá "ZBCZXFGHIX", que não é o resultado desejado. Além disso, eu não concordaria que isso seja mais simples do que as outras não- StringBuildersoluções postadas há dois anos e meio.
BACON