Remplacer plusieurs caractères dans une chaîne C #

178

Existe-t-il une meilleure façon de remplacer les chaînes?

Je suis surpris que Replace ne prenne pas dans un tableau de caractères ou un tableau de chaînes. Je suppose que je pourrais écrire ma propre extension, mais j'étais curieux de savoir s'il existe une meilleure façon intégrée de faire ce qui suit? Notez que le dernier remplacement est une chaîne et non un caractère.

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

Réponses:

206

Vous pouvez utiliser une expression régulière de remplacement.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ au début signifie une recherche
  • Les caractères entre [et ]sont les caractères à rechercher (dans n'importe quel ordre)
  • Le second /délimite le texte recherché et le texte de remplacement

En anglais, cela se lit comme suit:

"Recherchez ;ou ,ou \tou \rou (espace) ou exactement deux séquentiels \net remplacez-les par \n"

En C #, vous pouvez effectuer les opérations suivantes: (après l'importation System.Text.RegularExpressions)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");
johnluetke
la source
2
\tet \rsont inclus dans \s. Donc, votre expression régulière est équivalente à [;,\s].
NullUserException
3
Et cela \séquivaut en fait à [ \f\n\r\t\v]inclure des éléments qui ne figuraient pas dans la question initiale. De plus, la question originale demande Replace("\n\n", "\n")ce que votre regex ne gère pas.
NullUserException le
11
Veuillez noter que pour les opérations de remplacement simples qui ne sont pas configurables par un utilisateur, l'utilisation d'expressions régulières n'est pas optimale car elle est très lente par rapport aux opérations de chaîne régulières, selon un premier article de référence que j'ai trouvé lors de la recherche de "c # regex performance replace", c'est environ 13 fois plus lent.
trop
Ah regex, les hiéroglyphes du pouvoir! Le seul problème que je peux voir ici est la lisibilité humaine des expressions régulières; beaucoup refusent de les comprendre. J'ai récemment ajouté une solution ci-dessous pour ceux qui recherchent une alternative moins complexe.
sɐunıɔ ןɐ qɐp
Alors, comment écrire si nous voulons remplacer plusieurs caractères par plusieurs caractères?
Habip Oğuz
114

Si vous vous sentez particulièrement intelligent et ne souhaitez pas utiliser 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);

Vous pouvez également intégrer cela dans une méthode d'extension avec peu d'effort.

Edit: Ou attendez juste 2 minutes et je finirai par l'écrire quand même :)

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 );
   }
}

Et voilà ...

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

s = s.Replace(separators, "\n");
Murs Paul
la source
Très peu efficace en mémoire, en particulier pour les chaînes plus grandes
MarcinJuraszek
@MarcinJuraszek Lol ... C'est probablement la première fois que j'entends quelqu'un affirmer que les méthodes de chaîne intégrées sont moins efficaces en mémoire que les expressions régulières.
Paul Walls
10
Vous avez raison. J'aurais dû mesurer avant de publier ça. Je lance un benchmark et je suis Regex.Replaceplus de 8 fois plus lent que plusieurs string.Replaceappels consécutifs. et 4x plus lent que Split+ Join. Voir gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek
1
Belle solution! juste un petit addon. Malheureusement, cela ne fonctionnera pas si vous souhaitez que le ou les premiers caractères soient également remplacés. Supposons que vous souhaitiez remplacer le caractère «t» dans l'exemple de chaîne. La méthode Split supprimera simplement ce «t» du premier mot «this» car il s'agit d'un EmptyEntry. Si vous utilisez StringSplitOptions.None au lieu de RemoveEmptyEntries, Split laissera l'entrée et la méthode Join ajoutera le caractère de séparation à la place. J'espère que cela aide
Pierre
58

Vous pouvez utiliser la fonction Aggregate de 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'));

Voici la méthode d'extension:

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

Exemple d'utilisation de la méthode d'extension:

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

C'est le moyen le plus court:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");
ParPar
la source
1
Cette ligne unique aide également lorsque vous en avez besoin dans les initialiseurs.
Guney Ozsan
8

Ohhh, l'horreur de la performance! La réponse est un peu dépassée, mais quand même ...

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
la source
7

Les chaînes ne sont que des tableaux de caractères immuables

Il vous suffit de le rendre mutable:

  • soit en utilisant StringBuilder
  • aller dans le unsafemonde et jouer avec des pointeurs (dangereux cependant)

et essayez de parcourir le tableau de caractères le moins de fois possible. Notez l' HashSetici, car cela évite de parcourir la séquence de caractères à l'intérieur de la boucle. Si vous avez besoin d'une recherche encore plus rapide, vous pouvez la remplacer HashSetpar une recherche optimisée pour char(basée sur un array[256]).

Exemple avec 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;
        }
    }
}

Edit - Version optimisée

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;
        }
    }
}

Ensuite, utilisez-le simplement comme ceci:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();
Fabuleux
la source
N'oubliez pas que les chaînes sont wchar_ten .net, vous ne remplacez qu'un sous-ensemble de tous les caractères possibles (et vous aurez besoin de 65536 booléens pour optimiser cela ...)
gog
3

Vous pouvez également simplement écrire ces méthodes d'extension de chaîne et les placer quelque part dans votre solution:

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;
    }
}


Appelez-les comme ceci:

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

xyBxyDxy


Et ça:

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

xyCxyF

sɐunıɔ ןɐ qɐp
la source
2

Utilisez RegEx.Replace, quelque chose comme ceci:

  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);

Voici plus d'informations sur cette documentation MSDN pour RegEx.

Dmitry Samuylov
la source
1

Performance-Wise, ce n'est probablement pas la meilleure solution, mais cela fonctionne.

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}
Daniel Székely
la source
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
la source