Ersetzen Sie mehrere Zeichen in einer C # -String

177

Gibt es eine bessere Möglichkeit, Zeichenfolgen zu ersetzen?

Ich bin überrascht, dass Replace kein Zeichen- oder Zeichenfolgenarray akzeptiert. Ich denke, ich könnte meine eigene Erweiterung schreiben, aber ich war neugierig, ob es eine bessere Möglichkeit gibt, Folgendes zu tun? Beachten Sie, dass das letzte Ersetzen eine Zeichenfolge und kein Zeichen ist.

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

Antworten:

205

Sie können einen regulären Ausdruck zum Ersetzen verwenden.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ am Anfang bedeutet eine Suche
  • Die Zeichen zwischen [und] sind die Zeichen, nach denen gesucht werden soll (in beliebiger Reihenfolge).
  • Der zweite /begrenzt den Suchtext und den Ersetzungstext

Auf Englisch lautet dies:

"Suchen Sie nach ;oder ,oder \toder \roder (Leerzeichen) oder genau zwei aufeinanderfolgenden \nund ersetzen Sie es durch \n"

In C # können Sie Folgendes tun: (nach dem Importieren System.Text.RegularExpressions)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");
Johnluetke
quelle
2
\tund \rsind enthalten in \s. Ihre Regex entspricht also [;,\s].
NullUserException
3
Und \sist eigentlich gleichbedeutend damit, [ \f\n\r\t\v]dass Sie dort einige Dinge einfügen, die nicht in der ursprünglichen Frage enthalten waren. Außerdem wird in der ursprünglichen Frage gefragt, für Replace("\n\n", "\n")welche Regex Ihre Regex nicht geeignet ist .
NullUserException
11
Beachten Sie bitte, dass für einfache Ersetzungsoperationen, die von einem Benutzer nicht konfiguriert werden können, die Verwendung regulärer Ausdrücke nicht optimal ist, da sie im Vergleich zu regulären Zeichenfolgenoperationen sehr langsam ist. Dies geht aus einem ersten Benchmark-Artikel hervor, den ich bei der Suche nach "c # regex Performance Replace" gefunden habe mal langsamer.
Auch
Ah Regex, die Hieroglyphen der Macht! Das einzige Problem, das ich hier sehen kann, ist die menschliche Lesbarkeit der regulären Ausdrücke. viele weigern sich, sie zu verstehen. Ich habe kürzlich eine Lösung für diejenigen hinzugefügt, die nach einer weniger komplexen Alternative suchen.
sɐunıɔ ןɐ qɐp
Wie schreiben wir also, wenn wir mehrere Zeichen durch mehrere Zeichen ersetzen möchten?
Habip Oğuz
113

Wenn Sie sich besonders schlau fühlen und Regex nicht verwenden möchten:

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

Sie können dies auch mit geringem Aufwand in eine Erweiterungsmethode einwickeln.

Edit: Oder warte einfach 2 Minuten und ich werde es trotzdem schreiben :)

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

Und voila ...

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

s = s.Replace(separators, "\n");
Paul Walls
quelle
Sehr speichereffizient, insbesondere bei größeren Saiten.
MarcinJuraszek
@MarcinJuraszek Lol ... Das ist wahrscheinlich das erste Mal, dass ich jemanden behaupten hörte, dass die integrierten String-Methoden weniger speichereffizient sind als reguläre Ausdrücke.
Paul Walls
9
Du hast recht. Ich hätte messen sollen, bevor ich das gepostet habe. Ich führe einen Benchmark aus und bin Regex.Replaceüber 8x langsamer als mehrere string.ReplaceAnrufe hintereinander. und 4x langsamer als Split+ Join. Siehe gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek
1
Schöne Lösung! nur ein kleines Addon. Leider funktioniert dies nicht, wenn Sie möchten, dass auch die ersten Zeichen ersetzt werden. Angenommen, Sie möchten das Zeichen 't' in der Beispielzeichenfolge ersetzen. Die Split-Methode löscht einfach das 't' des ersten Wortes 'this', da es sich um einen EmptyEntry handelt. Wenn Sie StringSplitOptions.None anstelle von RemoveEmptyEntries verwenden, verlässt Split den Eintrag und die Join-Methode fügt stattdessen das Trennzeichen hinzu. Hoffe das hilft
Pierre
55

Sie können die Aggregatfunktion von Linq verwenden:

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

Hier ist die Erweiterungsmethode:

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

Anwendungsbeispiel für Erweiterungsmethoden:

string snew = s.ReplaceAll(chars, '\n');
dodgy_coder
quelle
20

Dies ist der kürzeste Weg:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");
ParPar
quelle
1
Dieser eine Liner hilft auch, wenn Sie dies in Initialisierern benötigen.
Guney Ozsan
7

Ohhh, der Performance-Horror! Die Antwort ist etwas veraltet, aber immer noch ...

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
quelle
5

Strings sind nur unveränderliche Char-Arrays

Sie müssen es nur veränderlich machen:

  • entweder mit StringBuilder
  • gehe in die unsafeWelt und spiele mit Zeigern (obwohl gefährlich)

und versuchen Sie, das Zeichenarray so oft wie möglich zu durchlaufen. Beachten Sie das HashSethier, da es vermieden wird, die Zeichenfolge innerhalb der Schleife zu durchlaufen. Wenn Sie eine noch schnellere Suche benötigen, können Sie diese HashSetdurch eine optimierte Suche nach char(basierend auf einer array[256]) ersetzen .

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

Bearbeiten - Optimierte Version

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

Dann benutzt du es einfach so:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();
Fab
quelle
Denken wchar_tSie daran, dass sich Strings in .net befinden. Sie ersetzen nur eine Teilmenge aller möglichen Zeichen (und Sie benötigen 65536 Bools, um dies zu optimieren ...)
gog
2

Sie können diese String-Erweiterungsmethoden auch einfach schreiben und irgendwo in Ihre Lösung einfügen:

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


Nennen Sie sie so:

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

xyBxyDxy


Und das:

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

xyCxyF

sɐunıɔ ןɐ qɐp
quelle
1

Verwenden Sie RegEx.Replace, ungefähr so:

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

Hier finden Sie weitere Informationen zu dieser MSDN-Dokumentation für RegEx.Replace

Dmitry Samuylov
quelle
0

In Bezug auf die Leistung ist dies wahrscheinlich nicht die beste Lösung, aber es funktioniert.

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

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