501 Stimmen

Effiziente Weise zu entfernen ALLE Leerzeichen aus String?

Ich rufe eine REST-API auf und erhalte eine XML-Antwort zurück. Sie gibt eine Liste von Arbeitsbereichsnamen zurück, und ich schreibe eine schnelle IsExistingWorkspace() Methode. Da alle Arbeitsbereiche aus zusammenhängenden Zeichen ohne Leerzeichen bestehen, nehme ich an, dass der einfachste Weg, um herauszufinden, ob ein bestimmter Arbeitsbereich in der Liste enthalten ist, darin besteht, alle Leerzeichen (einschließlich Zeilenumbrüche) zu entfernen und dies zu tun (XML ist die Zeichenkette, die von der Webanforderung empfangen wird):

XML.Contains("<name>" + workspaceName + "</name>");

Ich weiß, dass zwischen Groß- und Kleinschreibung unterschieden wird, und darauf verlasse ich mich. Ich brauche nur eine Möglichkeit, alle Leerzeichen in einer Zeichenfolge effizient zu entfernen. Ich weiß, RegEx und LINQ kann es tun, aber ich bin offen für andere Ideen. Ich bin vor allem nur über die Geschwindigkeit besorgt.

7 Stimmen

Das Parsen von XML mit Regex ist fast so schlimm wie Parsen von HTML mit Regex .

3 Stimmen

@henk holterman; Siehe meine Antwort unten, regexp scheint nicht in allen Fällen die schnellste zu sein.

0 Stimmen

Regex scheint nicht der schnellste zu sein. Ich habe die Ergebnisse vieler verschiedener Methoden zum Entfernen von Leerzeichen aus einer Zeichenkette zusammengefasst. Die Zusammenfassung ist in einer Antwort unten - stackoverflow.com/a/37347881/582061

822voto

slandau Punkte 22936

Dies ist der schnellste Weg, den ich kenne, auch wenn Sie sagten, Sie wollten keine regulären Ausdrücke verwenden:

Regex.Replace(XML, @"\s+", "");

In Anlehnung an @hypehuman in den Kommentaren: Wenn Sie dies mehr als einmal tun möchten, erstellen und speichern Sie eine Regex-Instanz. So sparen Sie sich den Overhead, den die Erstellung jedes Mal verursacht, was teurer ist, als Sie vielleicht denken.

private static readonly Regex sWhitespace = new Regex(@"\s+");
public static string ReplaceWhitespace(string input, string replacement) 
{
    return sWhitespace.Replace(input, replacement);
}

6 Stimmen

Ich könnte einen regulären Ausdruck verwenden, ich bin mir nur nicht sicher, ob das der schnellste Weg ist.

1 Stimmen

Ich bin ziemlich sicher, dass es so ist. Zumindest hinter den Kulissen müssen Sie jedes Zeichen überprüfen, und das ist nur eine lineare Suche.

0 Stimmen

Es gibt keinen schnelleren Weg, der einzige "andere" Weg ist, @"string".Replace(" ", string.Empty) für eine Million verschiedener Kombinationen zu verwenden. Regex erledigt das alles mit genau dieser Funktion.

252voto

Henk J Meulekamp Punkte 2609

Ich habe einen alternativen Weg ohne regexp, und es scheint ziemlich gut zu funktionieren. Es ist eine Fortsetzung der Antwort von Brandon Moretz:

 public static string RemoveWhitespace(this string input)
 {
    return new string(input.ToCharArray()
        .Where(c => !Char.IsWhiteSpace(c))
        .ToArray());
 }

Ich habe es in einem einfachen Einheitstest getestet:

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace1(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = input.RemoveWhitespace();
    }
    Assert.AreEqual(expected, s);
}

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace2(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = Regex.Replace(input, @"\s+", "");
    }
    Assert.AreEqual(expected, s);
}

Für 1.000.000 Versuche läuft die erste Option (ohne regexp) in weniger als einer Sekunde (700 ms auf meinem Rechner), die zweite dauert 3,5 Sekunden.

56 Stimmen

.ToCharArray() ist nicht notwendig; Sie können .Where() direkt auf eine Zeichenkette.

14 Stimmen

Ich möchte hier nur anmerken. Regex ist langsamer... bei kleinen Zeichenketten! Angenommen, Sie hätten eine digitalisierte Version eines Bandes über das US-Steuerrecht (~ Millionen Wörter?) mit einer Handvoll Iterationen, dann ist Regex bei weitem die beste Lösung! Es geht nicht darum, was schneller ist, sondern was unter welchen Umständen verwendet werden sollte. Sie haben hier nur die Hälfte der Gleichung bewiesen. -Solange, bis Sie die zweite Hälfte des Tests beweisen, so dass die Antwort mehr Aufschluss darüber gibt, wann was verwendet werden sollte.

21 Stimmen

@ppumkin Er bat um eine einmalige Entfernung von Leerzeichen. Nicht mehrere Iterationen anderer Verarbeitungen. Ich werde die Entfernung von Leerzeichen in einem einzigen Durchgang nicht zu einem ausführlichen Beitrag über Benchmarking von Textverarbeitung machen.

126voto

Mike_K Punkte 8300

Versuchen Sie die Methode replace der Zeichenfolge in C#.

XML.Replace(" ", string.Empty);

34 Stimmen

Entfernt keine Tabulatoren oder Zeilenumbrüche. Wenn ich jetzt mehrere Entfernungen vornehme, mache ich mehrere Durchgänge über die Zeichenfolge.

0 Stimmen

@MattSach warum werden nicht ALLE Leerzeichen entfernt?

4 Stimmen

@Zapnologica Es werden nur Leerzeichen ersetzt. Der Auftraggeber hat auch um die Ersetzung von Zeilenumbrüchen gebeten (die "Leerzeichen" sind, auch wenn sie keine Leerzeichen sind).

111voto

kernowcode Punkte 5194

Meine Lösung ist die Verwendung von Split and Join, und sie ist überraschend schnell, nämlich die schnellste der Top-Antworten hier.

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));

Zeiten für 10.000 Schleifen bei einer einfachen Zeichenkette mit Leerzeichen einschließlich Zeilenumbrüchen und Tabulatoren

  • Teilen/Verbinden = 60 Millisekunden
  • linq chararray = 94 Millisekunden
  • regex = 437 Millisekunden

Verbessern Sie dies, indem Sie es in eine Methode verpacken, um ihm Bedeutung zu verleihen, und machen Sie es zu einer Erweiterungsmethode, wenn wir schon dabei sind ...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}

4 Stimmen

Diese Lösung gefällt mir sehr gut. Ich verwende eine ähnliche Lösung seit der Zeit vor LINQ. Ich bin eigentlich mit LINQs Leistung beeindruckt, und etwas überrascht mit Regex. Vielleicht war der Code nicht so optimal, wie er für Regex hätte sein können (Sie müssen zum Beispiel das Regex-Objekt zwischenspeichern). Aber der Kern des Problems ist, dass die "Qualität" der Daten eine große Rolle spielt. Bei langen Zeichenketten wird die Regex vielleicht besser abschneiden als die anderen Optionen. Es wird ein lustiger Benchmark sein... :-)

2 Stimmen

Wie funktioniert default(string[]) == eine Liste mit allen Leerzeichen? Ich sehe, dass es funktioniert, aber ich verstehe nicht wie?

0 Stimmen

Split benötigt ein gültiges Array und null reicht nicht aus. default(type), in diesem Fall ein string[], gibt die korrekte Vorgabe für die Funktion zurück.

65voto

Stian Standahl Punkte 2364

Aufbauend auf Henks Antwort Ich habe einige Testmethoden mit seiner Antwort und einige zusätzliche, optimierte Methoden erstellt. Ich habe festgestellt, dass die Ergebnisse von der Größe der Eingabezeichenfolge abhängen. Daher habe ich mit zwei Ergebnismengen getestet. Bei der schnellsten Methode ist die verknüpfte Quelle sogar noch schneller. Da sie aber als unsicher eingestuft wird, habe ich sie ausgelassen.

Lange Eingabezeichenfolge ergibt:

  1. InPlaceCharArray: 2021 ms ( Die Antwort von Sunsetquest ) - ( Ursprüngliche Quelle )
  2. Zeichenkette aufteilen und verbinden: 4277ms ( Antwort von Kernowcode )
  3. String-Leser: 6082 ms
  4. LINQ mit nativem char.IsWhitespace: 7357 ms
  5. LINQ: 7746 ms ( Henks Antwort )
  6. ForLoop: 32320 ms
  7. RegexCompiled: 37157 ms
  8. Regex: 42940 ms

Kurze Eingabezeichenfolge ergibt:

  1. InPlaceCharArray: 108 ms ( Die Antwort von Sunsetquest ) - ( Ursprüngliche Quelle )
  2. Zeichenkette aufteilen und verbinden: 294 ms ( Antwort von Kernowcode )
  3. String-Leser: 327 ms
  4. ForLoop: 343 ms
  5. LINQ mit nativem char.IsWhitespace: 624 ms
  6. LINQ: 645ms ( Henks Antwort )
  7. RegexCompiled: 1671 ms
  8. Regex: 2599 ms

Code :

public class RemoveWhitespace
{
    public static string RemoveStringReader(string input)
    {
        var s = new StringBuilder(input.Length); // (input.Length);
        using (var reader = new StringReader(input))
        {
            int i = 0;
            char c;
            for (; i < input.Length; i++)
            {
                c = (char)reader.Read();
                if (!char.IsWhiteSpace(c))
                {
                    s.Append(c);
                }
            }
        }

        return s.ToString();
    }

    public static string RemoveLinqNativeCharIsWhitespace(string input)
    {
        return new string(input.ToCharArray()
            .Where(c => !char.IsWhiteSpace(c))
            .ToArray());
    }

    public static string RemoveLinq(string input)
    {
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }

    public static string RemoveRegex(string input)
    {
        return Regex.Replace(input, @"\s+", "");
    }

    private static Regex compiled = new Regex(@"\s+", RegexOptions.Compiled);
    public static string RemoveRegexCompiled(string input)
    {
        return compiled.Replace(input, "");
    }

    public static string RemoveForLoop(string input)
    {
        for (int i = input.Length - 1; i >= 0; i--)
        {
            if (char.IsWhiteSpace(input[i]))
            {
                input = input.Remove(i, 1);
            }
        }
        return input;
    }

    public static string StringSplitThenJoin(this string str)
    {
        return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
    }

    public static string RemoveInPlaceCharArray(string input)
    {
        var len = input.Length;
        var src = input.ToCharArray();
        int dstIdx = 0;
        for (int i = 0; i < len; i++)
        {
            var ch = src[i];
            switch (ch)
            {
                case '\u0020':
                case '\u00A0':
                case '\u1680':
                case '\u2000':
                case '\u2001':
                case '\u2002':
                case '\u2003':
                case '\u2004':
                case '\u2005':
                case '\u2006':
                case '\u2007':
                case '\u2008':
                case '\u2009':
                case '\u200A':
                case '\u202F':
                case '\u205F':
                case '\u3000':
                case '\u2028':
                case '\u2029':
                case '\u0009':
                case '\u000A':
                case '\u000B':
                case '\u000C':
                case '\u000D':
                case '\u0085':
                    continue;
                default:
                    src[dstIdx++] = ch;
                    break;
            }
        }
        return new string(src, 0, dstIdx);
    }
}

Tests :

[TestFixture]
public class Test
{
    // Short input
    //private const string input = "123 123 \t 1adc \n 222";
    //private const string expected = "1231231adc222";

    // Long input
    private const string input = "123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222";
    private const string expected = "1231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc222";

    private const int iterations = 1000000;

    [Test]
    public void RemoveInPlaceCharArray()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveInPlaceCharArray(input);
        }

        stopwatch.Stop();
        Console.WriteLine("InPlaceCharArray: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveStringReader()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveStringReader(input);
        }

        stopwatch.Stop();
        Console.WriteLine("String reader: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveLinqNativeCharIsWhitespace()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveLinqNativeCharIsWhitespace(input);
        }

        stopwatch.Stop();
        Console.WriteLine("LINQ using native char.IsWhitespace: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveLinq()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveLinq(input);
        }

        stopwatch.Stop();
        Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds + " ms");
        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveRegex()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveRegex(input);
        }

        stopwatch.Stop();
        Console.WriteLine("Regex: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveRegexCompiled()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveRegexCompiled(input);
        }

        stopwatch.Stop();
        Console.WriteLine("RegexCompiled: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [Test]
    public void RemoveForLoop()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.RemoveForLoop(input);
        }

        stopwatch.Stop();
        Console.WriteLine("ForLoop: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }

    [TestMethod]
    public void StringSplitThenJoin()
    {
        string s = null;
        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            s = RemoveWhitespace.StringSplitThenJoin(input);
        }

        stopwatch.Stop();
        Console.WriteLine("StringSplitThenJoin: " + stopwatch.ElapsedMilliseconds + " ms");

        Assert.AreEqual(expected, s);
    }
}

bearbeiten : Ich habe einen netten One-Liner von Kernowcode getestet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X