Substituir vários caracteres em uma string C #

178

Existe uma maneira melhor de substituir as cordas?

Estou surpreso que Substituir não leva em uma matriz de caracteres ou matriz de seqüência de caracteres. Acho que eu poderia escrever minha própria extensão, mas fiquei curioso para saber se existe uma maneira melhor de fazer o seguinte? Observe que o último Substituir é uma sequência e não um caractere.

myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");
zgirod
fonte

Respostas:

206

Você pode usar uma expressão regular de substituição.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ no começo significa uma pesquisa
  • Os caracteres entre [e ]são os caracteres a serem pesquisados ​​(em qualquer ordem)
  • O segundo /delimita o texto de pesquisa e o texto de substituição

Em inglês, lê-se:

"Procurar ;ou ,ou \tou \rou (espaço) ou exatamente dois sequencial \ne substituí-lo por \n"

Em C #, você pode fazer o seguinte: (após a importação System.Text.RegularExpressions)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");
johnluetke
fonte
2
\te \restão incluídos no \s. Portanto, seu regex é equivalente a [;,\s].
NullUserException
3
E \sé realmente equivalente a [ \f\n\r\t\v]que você esteja incluindo algumas coisas que não estavam na pergunta original. Além disso, a pergunta original pergunta para Replace("\n\n", "\n")qual seu regex não lida.
NullUserException
11
Por favor, considere que, para operações simples de substituição que não são configuráveis ​​por um usuário, o uso de expressões regulares não é ideal, pois é muito lento em comparação com as operações regulares de string, de acordo com um primeiro artigo de benchmark que encontrei ao pesquisar "c # regex performance replace", é cerca de 13 vezes mais devagar.
também
Ah, regex, os hieróglifos do poder! O único problema que posso ver aqui é a legibilidade humana das expressões regulares; muitos se recusam a entendê-los. Recentemente, adicionei uma solução abaixo para quem procura uma alternativa menos complexa.
sɐunıɔ ןɐ qɐp
Então, como escrevemos se queremos substituir vários caracteres por vários caracteres?
Habip Oğuz 24/12/19
114

Se você está se sentindo particularmente inteligente e não deseja usar o Regex:

char[] separators = new char[]{' ',';',',','\r','\t','\n'};

string s = "this;is,\ra\t\n\n\ntest";
string[] temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
s = String.Join("\n", temp);

Você pode agrupar isso em um método de extensão com pouco esforço também.

Edit: Ou apenas espere 2 minutos e eu acabarei escrevendo de qualquer maneira :)

public static class ExtensionMethods
{
   public static string Replace(this string s, char[] separators, string newVal)
   {
       string[] temp;

       temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
       return String.Join( newVal, temp );
   }
}

E pronto ...

char[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";

s = s.Replace(separators, "\n");
Paul Walls
fonte
Muito ineficiente de memória, especialmente para cadeias maiores.
MarcinJuraszek
@MarcinJuraszek Lol ... Essa é provavelmente a primeira vez que ouvi alguém afirmar que os métodos internos de string são menos eficientes em termos de memória do que expressões regulares.
Paul Wall
10
Você está certo. Eu deveria ter medido antes de postar isso. Eu corro o benchmark e Regex.Replaceé 8 vezes mais lento que várias string.Replacechamadas seguidas. e 4x mais lento que Split+ Join. Veja gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek
1
Ótima solução! apenas um pequeno complemento. Infelizmente, isso não funcionará se você desejar que os primeiros caracteres sejam substituídos também. Digamos que você queira substituir o caractere 't' na sequência de exemplo. O método Split simplesmente descartará o 't' da primeira palavra 'this' porque é um EmptyEntry. Se você usar o StringSplitOptions.None em vez de RemoveEmptyEntries, Split deixará a entrada e o método Join adicionará o caractere separador. Espero que isso ajude
Pierre
58

Você pode usar a função Agregada do Linq:

string s = "the\nquick\tbrown\rdog,jumped;over the lazy fox.";
char[] chars = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
string snew = chars.Aggregate(s, (c1, c2) => c1.Replace(c2, '\n'));

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

public static string ReplaceAll(this string seed, char[] chars, char replacementCharacter)
{
    return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, replacementCharacter));
}

Exemplo de uso do método de extensão:

string snew = s.ReplaceAll(chars, '\n');
dodgy_coder
fonte
21

Esta é a maneira mais curta:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");
ParPar
fonte
1
Esse liner também ajuda quando você precisa disso nos inicializadores.
precisa saber é o seguinte
8

Ohhh, o horror da performance! A resposta está um pouco desatualizada, mas ainda assim ...

public static class StringUtils
{
    #region Private members

    [ThreadStatic]
    private static StringBuilder m_ReplaceSB;

    private static StringBuilder GetReplaceSB(int capacity)
    {
        var result = m_ReplaceSB;

        if (null == result)
        {
            result = new StringBuilder(capacity);
            m_ReplaceSB = result;
        }
        else
        {
            result.Clear();
            result.EnsureCapacity(capacity);
        }

        return result;
    }


    public static string ReplaceAny(this string s, char replaceWith, params char[] chars)
    {
        if (null == chars)
            return s;

        if (null == s)
            return null;

        StringBuilder sb = null;

        for (int i = 0, count = s.Length; i < count; i++)
        {
            var temp = s[i];
            var replace = false;

            for (int j = 0, cc = chars.Length; j < cc; j++)
                if (temp == chars[j])
                {
                    if (null == sb)
                    {
                        sb = GetReplaceSB(count);
                        if (i > 0)
                            sb.Append(s, 0, i);
                    }

                    replace = true;
                    break;
                }

            if (replace)
                sb.Append(replaceWith);
            else
                if (null != sb)
                    sb.Append(temp);
        }

        return null == sb ? s : sb.ToString();
    }
}
John Whiter
fonte
7

Strings são apenas matrizes de caracteres imutáveis

Você só precisa torná-lo mutável:

  • usando StringBuilder
  • vá ao unsafemundo e brinque com ponteiros (embora perigoso)

e tente iterar pela matriz de caracteres a menor quantidade de vezes. Observe o HashSetaqui, pois evita atravessar a sequência de caracteres dentro do loop. Se você precisar de uma pesquisa ainda mais rápida, poderá substituir HashSetpor uma pesquisa otimizada para char(com base em um array[256]).

Exemplo com StringBuilder

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace, 
    char replacement)
{
    HashSet<char> set = new HashSet<char>(toReplace);
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set.Contains(currentCharacter))
        {
            builder[i] = replacement;
        }
    }
}

Editar - versão otimizada

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace,
    char replacement)
{
    var set = new bool[256];
    foreach (var charToReplace in toReplace)
    {
        set[charToReplace] = true;
    }
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set[currentCharacter])
        {
            builder[i] = replacement;
        }
    }
}

Então você apenas o usa assim:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();
Fab
fonte
Lembre-se que as cordas estão wchar_tem .net, você está substituindo apenas um subconjunto de todos os possíveis caracteres (e você vai precisar 65536 bools para otimizar isso ...)
gog
3

Você também pode simplesmente escrever esses métodos de extensão de string e colocá-los em algum lugar da sua solução:

using System.Text;

public static class StringExtensions
{
    public static string ReplaceAll(this string original, string toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(toBeReplaced)) return original;
        if (newValue == null) newValue = string.Empty;
        StringBuilder sb = new StringBuilder();
        foreach (char ch in original)
        {
            if (toBeReplaced.IndexOf(ch) < 0) sb.Append(ch);
            else sb.Append(newValue);
        }
        return sb.ToString();
    }

    public static string ReplaceAll(this string original, string[] toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || toBeReplaced == null || toBeReplaced.Length <= 0) return original;
        if (newValue == null) newValue = string.Empty;
        foreach (string str in toBeReplaced)
            if (!string.IsNullOrEmpty(str))
                original = original.Replace(str, newValue);
        return original;
    }
}


Chame-os assim:

"ABCDE".ReplaceAll("ACE", "xy");

xyBxyDxy


E isto:

"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");

xyCxyF

sɐunıɔ ןɐ qɐp
fonte
2

Use RegEx.Replace, algo como isto:

  string input = "This is   text with   far  too   much   " + 
                 "whitespace.";
  string pattern = "[;,]";
  string replacement = "\n";
  Regex rgx = new Regex(pattern);
  string result = rgx.Replace(input, replacement);

Aqui estão mais informações sobre esta documentação do MSDN para o RegEx.

Dmitry Samuylov
fonte
1

Performance-Wise, isso provavelmente pode não ser a melhor solução, mas funciona.

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}
Daniel Székely
fonte
1
string ToBeReplaceCharacters = @"~()@#$%&amp;+,'&quot;&lt;&gt;|;\/*?";
string fileName = "filename;with<bad:separators?";

foreach (var RepChar in ToBeReplaceCharacters)
{
    fileName = fileName.Replace(RepChar.ToString(), "");
}
Jignesh Bhayani
fonte