Effiziente Möglichkeit, ALLE Leerzeichen aus dem String zu entfernen?

358

Ich rufe eine REST-API auf und erhalte eine XML-Antwort zurück. Es gibt eine Liste der Namen eines Arbeitsbereichs zurück, und ich schreibe eine schnelle IsExistingWorkspace()Methode. Da alle Arbeitsbereiche aus zusammenhängenden Zeichen ohne Leerzeichen bestehen, gehe ich davon aus, dass der einfachste Weg, um herauszufinden, ob sich ein bestimmter Arbeitsbereich in der Liste befindet, darin besteht, alle Leerzeichen (einschließlich Zeilenumbrüche) zu entfernen und dies zu tun (XML ist die vom Web empfangene Zeichenfolge) Anfrage):

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 einem String effizient zu entfernen. Ich weiß, dass RegEx und LINQ das können, aber ich bin offen für andere Ideen. Ich mache mir meistens nur Sorgen um die Geschwindigkeit.

Corey Ogburn
quelle
6
Das Parsen von XML mit Regex ist fast so schlecht wie das Parsen von HTML mit Regex .
dtb
3
@henk holterman; Siehe meine Antwort unten, Regexp scheint nicht in allen Fällen die schnellste zu sein.
Henk J Meulekamp
Regex scheint überhaupt nicht der schnellste zu sein. Ich habe die Ergebnisse auf viele verschiedene Arten zusammengefasst, um Leerzeichen aus einer Zeichenfolge zu entfernen. Die Zusammenfassung ist in einer Antwort unten - stackoverflow.com/a/37347881/582061
Stian Standahl

Antworten:

616

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

Regex.Replace(XML, @"\s+", "")
slandau
quelle
1
Ich könnte einen regulären Ausdruck verwenden, ich bin mir nur nicht sicher, ob es der schnellste Weg ist.
Corey Ogburn
1
Ich bin mir ziemlich sicher, dass es so ist. Zumindest hinter den Kulissen muss man jedes Zeichen überprüfen, und dies ist nur eine lineare Suche.
Slandau
19
Sollte das nicht sein Regex.Replace(XML, @"\s+", "")?
Jan-Peter Vos
61
Wenn Sie dies mehrmals planen, erstellen und speichern Sie eine Regex-Instanz. Dies spart den Aufwand für die Erstellung jedes Mal, 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); }
Hypehuman
10
Für diejenigen, die RegEx noch nicht kennen und nach einer Erklärung suchen, was dieser Ausdruck bedeutet, \sbedeutet dies "mit einem beliebigen Leerzeichen übereinstimmen" und +"mit einem oder mehreren der fortlaufenden Token übereinstimmen". Auch RegExr ist eine schöne Website zu schreiben RegEx Ausdrücke mit üben, wenn Sie experimentieren wollen.
jrh
181

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

Bei 1.000.000 Versuchen dauert die erste Option (ohne regulären Ausdruck) weniger als eine Sekunde (700 ms auf meinem Computer), und die zweite dauert 3,5 Sekunden.

Henk J Meulekamp
quelle
40
.ToCharArray()ist nicht nötig; Sie können .Where()direkt auf einer Zeichenfolge verwenden.
ProgramFOX
10
Nur hier zu beachten. Regex ist langsamer ... auf kleinen Saiten! Wenn Sie sagen, Sie hätten eine digitalisierte Version eines Bandes zum US-Steuerrecht (~ Millionen Wörter?) Mit einer Handvoll Iterationen, ist Regex bei weitem König! Es ist nicht was schneller ist, sondern was unter welchen Umständen verwendet werden sollte. Sie haben hier nur die halbe Gleichung bewiesen. -1, bis Sie die zweite Hälfte des Tests beweisen, damit die Antwort mehr Aufschluss darüber gibt, wann was verwendet werden sollte.
Piotr Kula
17
@ppumkin Er bat um eine einmalige Entfernung von Leerzeichen. Nicht mehrere Iterationen anderer Verarbeitung. Ich werde diese Whitespace-Entfernung in einem Durchgang nicht zu einem erweiterten Beitrag über das Benchmarking der Textverarbeitung machen.
Henk J Meulekamp
1
Sie sagten, es sei vorzuziehen, diesmal nicht zur Regex zu verwenden, sagten aber nicht warum.
Piotr Kula
2
@ProgramFOX, in einer anderen Frage (kann es nicht leicht finden) habe ich festgestellt, dass zumindest in einigen Abfragen die Verwendung ToCharArrayschneller ist als die .Where()direkte Verwendung in der Zeichenfolge. Dies hat etwas mit dem Overhead in IEnumerable<>jedem Iterationsschritt zu tun , und die Tatsache, ToCharArraydass es sehr effizient ist (Blockkopie) und der Compiler die Iteration über Arrays optimiert. Warum dieser Unterschied besteht, konnte mir niemand erklären, sondern messen, bevor Sie entfernen ToCharArray().
Abel
87

Versuchen Sie die Ersetzungsmethode der Zeichenfolge in C #.

XML.Replace(" ", string.Empty);
Mike_K
quelle
28
Entfernt keine Tabulatoren oder Zeilenumbrüche. Wenn ich jetzt mehrere Entfernungen mache, mache ich mehrere Durchgänge über die Zeichenfolge.
Corey Ogburn
11
Ablehnung dafür, dass nicht alle Leerzeichen entfernt wurden, wie es die Antworten von slandau und Henk tun.
Matt Sach
@MattSach warum werden nicht ALLE Leerzeichen entfernt?
Zapnologica
4
@Zapnologica Ersetzt nur Leerzeichen. Das OP forderte auch das Ersetzen von Zeilenumbrüchen (die "Leerzeichen" sind, obwohl sie keine Leerzeichen sind).
Matt Sach
75

Meine Lösung besteht darin, Split and Join zu verwenden, und es ist überraschend schnell, tatsächlich die schnellste der Top-Antworten hier.

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

Timings für 10.000 Schleifen auf einfachen Zeichenfolgen mit Leerzeichen inkl. Neuen Zeilen und Tabulatoren

  • split / join = 60 Millisekunden
  • linq chararray = 94 Millisekunden
  • Regex = 437 Millisekunden

Verbessern Sie dies, indem Sie es in eine Methode einwickeln, um ihm eine Bedeutung zu geben, und machen Sie es auch zu einer Erweiterungsmethode, während wir gerade dabei sind ...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
Kernowcode
quelle
3
Ich mag diese Lösung wirklich, ich verwende seit Tagen vor LINQ eine ähnliche. Ich bin wirklich beeindruckt von der Leistung von LINQs und etwas überrascht von Regex. Möglicherweise war der Code nicht so optimal wie für Regex (Sie müssen beispielsweise das Regex-Objekt zwischenspeichern). Der Kern des Problems ist jedoch, dass die "Qualität" der Daten eine große Rolle spielt. Vielleicht übertrifft der Regex bei langen Saiten die anderen Optionen. Es wird ein lustiger Benchmark sein ... :-)
Loudenvier
1
Wie funktioniert default (string []) == eine Liste aller Leerzeichen? Ich sehe es funktionieren, aber ich verstehe nicht wie?
Jake Drew
5
@kernowcode Du meinst die Mehrdeutigkeit zwischen den 2 Überladungen mit string[]und char[]? Sie müssen nur angeben, welche Sie möchten, z string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. Genau das macht Ihr Aufruf defaultin diesem Fall, da er ebenfalls zurückkehrt null: Er hilft dem Compiler bei der Entscheidung, welche Überladung ausgewählt werden soll. Daher mein Kommentar, weil die Aussage in Ihrem Kommentar "Split benötigt ein gültiges Array und null reicht nicht ..." falsch ist. Keine große Sache, nur erwähnenswert, da Jake Drew fragte, wie das funktioniert. +1 für Ihre Antwort
Frank J
6
Coole Idee ... aber ich würde es wie folgt machen:string.Concat("H \ne llo Wor ld".Split())
Michael Krisper
3
Michaelkrisper Lösung ist sehr gut lesbar. Ich habe einen Test durchgeführt und 'split / join' (162 Millisekunden) schnitt bei 10.000 Iterationen derselben Zeichenfolge besser ab als 'split / concat' (180 Millisekunden).
Kernowcode
45

Aufbauend auf Henks Antwort ich einige Testmethoden mit seiner Antwort erstellt und einige hinzugefügte, optimierte Methoden. Ich habe festgestellt, dass die Ergebnisse je nach Größe der Eingabezeichenfolge unterschiedlich sind. Deshalb habe ich mit zwei Ergebnismengen getestet. Bei der schnellsten Methode hat die verknüpfte Quelle einen noch schnelleren Weg. Aber da es als unsicher eingestuft wird, habe ich dies weggelassen.

Lange Eingabezeichenfolge Ergebnisse:

  1. InPlaceCharArray: 2021 ms ( Antwort von Sunsetquest ) - ( Originalquelle )
  2. String teilen und dann 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 Ergebnisse der Eingabezeichenfolge:

  1. InPlaceCharArray: 108 ms ( Antwort von Sunsetquest ) - ( Originalquelle )
  2. String teilen, dann 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: 645 ms (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 : Getestet einen schönen Einzeiler von Kernowcode.

Stian Standahl
quelle
24

Nur eine Alternative, weil es ganz gut aussieht :) - HINWEIS : Henks Antwort ist die schnellste davon.

input.ToCharArray()
 .Where(c => !Char.IsWhiteSpace(c))
 .Select(c => c.ToString())
 .Aggregate((a, b) => a + b);

Testen von 1.000.000 Schleifen "This is a simple Test"

Diese Methode = 1,74 Sekunden
Regex = 2,58 Sekunden
new String(Henks) = 0,82

BlueChippy
quelle
1
Warum wurde das abgelehnt? Es ist vollkommen akzeptabel, erfüllt die Anforderungen, arbeitet schneller als die RegEx-Option und ist sehr gut lesbar?
BlueChippy
4
weil es viel kürzer geschrieben werden kann: neuer String (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit
7
Könnte wahr sein - aber die Antwort bleibt bestehen, ist lesbar, schneller als Regex und liefert das gewünschte Ergebnis. Viele der anderen Antworten sind NACH dieser ... daher macht eine Abwertung keinen Sinn.
BlueChippy
2
Gibt es eine Einheit für "0,82"? Oder ist es ein relatives Maß (82%)? Können Sie Ihre Antwort bearbeiten, um sie klarer zu machen?
Peter Mortensen
20

Ich habe auf CodeProject von Felipe Machado (mit Hilfe von Richard Robertson ) eine schöne Zusammenfassung dazu gefunden.

Er testete zehn verschiedene Methoden. Dies ist die schnellste unsichere Version ...

public static unsafe string TrimAllWithStringInplace(string str) {
    fixed (char* pfixed = str) {
        char* dst = pfixed;
        for (char* p = pfixed; *p != 0; p++)

            switch (*p) {

                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:
                    *dst++ = *p;
                    break;
            }

        return new string(pfixed, 0, (int)(dst - pfixed));
    }
}

Und die schnellste sichere Version ...

public static string TrimAllWithInplaceCharArray(string str) {

    var len = str.Length;
    var src = str.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);
}

Es gibt auch einige nette unabhängige Benchmarks zu Stack Overflow von Stian Standahl, die auch zeigen, dass Felipes Funktion etwa 300% schneller ist als die nächstschnellste Funktion.

Sunsetquest
quelle
Ich habe versucht, dies in C ++ zu übersetzen, bin aber ein wenig festgefahren. Irgendwelche Ideen, warum mein Port ausfällt? stackoverflow.com/questions/42135922/…
Jon Cage
2
Ich kann nicht widerstehen. Schauen Sie in den Kommentaren des Artikels nach, auf den Sie sich beziehen. Sie finden mich als "Basketcase Software". Er und haben eine Weile zusammen daran gearbeitet. Ich hatte das völlig vergessen, als dieses Problem wieder auftauchte. Danke für gute Erinnerungen. :)
Richard Robertson
1
Und was ist, wenn Sie nur zusätzliches WS entfernen möchten? Was ist mit diesem stackoverflow.com/questions/17770202/… Mod?
Tom
Am schnellsten ist es etwas langsamer ;-) String als Container-Leistung hier besser (in App 4:15 bis 3:55 => 8,5% weniger, aber wenn links String 3:30 => 21,4% weniger und Profiller zeigt rund 50% ausgegeben in diese Methode). In echten Live-Strings sollte der String also etwa 40% schneller sein als die hier verwendete (langsame) Array-Konvertierung.
Tom
15

Wenn Sie eine hervorragende Leistung benötigen, sollten Sie in diesem Fall LINQ und reguläre Ausdrücke vermeiden. Ich habe ein Performance-Benchmarking durchgeführt, und es scheint, dass string.Trim () Ihre ultimative Funktion ist, wenn Sie Leerzeichen vom Anfang und Ende des Strings entfernen möchten.

Wenn Sie alle Leerzeichen von einer Zeichenfolge entfernen müssen, funktioniert die folgende Methode am schnellsten von allen hier veröffentlichten:

    public static string RemoveWhitespace(this string input)
    {
        int j = 0, inputlen = input.Length;
        char[] newarr = new char[inputlen];

        for (int i = 0; i < inputlen; ++i)
        {
            char tmp = input[i];

            if (!char.IsWhiteSpace(tmp))
            {
                newarr[j] = tmp;
                ++j;
            }
        }
        return new String(newarr, 0, j);
    }
JHM
quelle
Ich wäre neugierig auf die Details Ihrer Benchmarkings - nicht, dass ich skeptisch bin, aber ich bin neugierig auf den Overhead, der mit Linq verbunden ist. Wie schlimm war es
Mark Meuer
Ich habe nicht alle Tests wiederholt, aber ich kann mich noch gut daran erinnern: Alles, was Linq betraf, war viel langsamer als alles ohne. Die clevere Verwendung von String / Char-Funktionen und Konstruktoren machte keinen prozentualen Unterschied, wenn Linq verwendet wurde.
JHM
11

Regex ist übertrieben; Verwenden Sie einfach die Erweiterung für die Zeichenfolge (danke Henk). Dies ist trivial und sollte Teil des Rahmens sein. Wie auch immer, hier ist meine Implementierung:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}
Maksood
quelle
Dies ist im Grunde eine unnötige Antwort (Regex ist übertrieben, aber eine schnellere Lösung als gegeben - und es wird bereits akzeptiert?)
W1ll1amvl
Wie können Sie Linq-Erweiterungsmethoden für eine Zeichenfolge verwenden? Ich kann nicht herausfinden, mit welcher ich andere vermisse alsSystem.Linq
GGirard
Ok sieht so aus, als ob dies in PCL nicht verfügbar ist, IEnumerable <char> ist in der Microsoft String-Implementierung bedingt ... Und ich verwende Profile259, das dies nicht unterstützt :)
GGirard
4

Hier ist eine einfache lineare Alternative zur RegEx-Lösung. Ich bin mir nicht sicher, was schneller ist; Sie müssten es messen.

static string RemoveWhitespace(string input)
{
    StringBuilder output = new StringBuilder(input.Length);

    for (int index = 0; index < input.Length; index++)
    {
        if (!Char.IsWhiteSpace(input, index))
        {
            output.Append(input[index]);
        }
    }
    return output.ToString();
}
Brandon Moretz
quelle
3

Ich musste Leerzeichen in einer Zeichenfolge durch Leerzeichen ersetzen, aber keine doppelten Leerzeichen. Ich musste beispielsweise Folgendes konvertieren:

"a b   c\r\n d\t\t\t e"

zu

"a b c d e"

Ich habe die folgende Methode verwendet

private static string RemoveWhiteSpace(string value)
{
    if (value == null) { return null; }
    var sb = new StringBuilder();

    var lastCharWs = false;
    foreach (var c in value)
    {
        if (char.IsWhiteSpace(c))
        {
            if (lastCharWs) { continue; }
            sb.Append(' ');
            lastCharWs = true;
        }
        else
        {
            sb.Append(c);
            lastCharWs = false;
        }
    }
    return sb.ToString();
}
user1325543
quelle
2

Ich gehe davon aus, dass Ihre XML-Antwort folgendermaßen aussieht:

var xml = @"<names>
                <name>
                    foo
                </name>
                <name>
                    bar
                </name>
            </names>";

Der beste Weg, XML zu verarbeiten, ist die Verwendung eines XML-Parsers wie LINQ to XML :

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");
dtb
quelle
Sobald ich überprüft habe, ob ein bestimmtes <Name> -Tag den richtigen Wert hat, bin ich fertig. Hätte das Parsen des Dokuments nicht einen gewissen Aufwand?
Corey Ogburn
4
Sicher, es hat etwas Overhead. Aber es hat den Vorteil, richtig zu sein. Eine Lösung, die zB auf Regex basiert, ist viel schwieriger zu finden. Wenn Sie feststellen, dass eine LINQ to XML-Lösung zu langsam ist, können Sie sie jederzeit durch eine schnellere ersetzen. Sie sollten jedoch vermeiden, nach der effizientesten Implementierung zu suchen, bevor Sie wissen, dass die richtige zu langsam ist.
dtb
Dies wird auf den Backend-Servern meines Arbeitgebers ausgeführt. Leichtgewicht ist das, wonach ich suche. Ich möchte nichts, was "nur funktioniert", aber optimal ist.
Corey Ogburn
4
LINQ to XML ist eine der
einfachsten
1

Hier ist noch eine andere Variante:

public static string RemoveAllWhitespace(string aString)
{
  return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}

Wie bei den meisten anderen Lösungen habe ich keine umfassenden Benchmark-Tests durchgeführt, aber dies funktioniert für meine Zwecke gut genug.

Fred
quelle
1

Wir können benutzen:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }
Tarik BENARAB
quelle
Dies ist fast genau das gleiche wie Henks Antwort oben. Der einzige Unterschied besteht darin, dass Sie nach suchen null.
Corey Ogburn
Ja, überprüfen Sie, ob null wichtig ist
Tarik BENARAB
1
Vielleicht hätte dies nur ein Kommentar zu seiner Antwort sein sollen. Ich bin froh, dass du es angesprochen hast. Ich wusste nicht, dass Erweiterungsmethoden für Nullobjekte aufgerufen werden können.
Corey Ogburn
0

Ich habe festgestellt, dass unterschiedliche Ergebnisse wahr sind. Ich versuche, alle Leerzeichen durch ein einzelnes Leerzeichen zu ersetzen, und der reguläre Ausdruck war extrem langsam.

return( Regex::Replace( text, L"\s+", L" " ) );

Was für mich (in C ++ cli) am besten funktioniert hat, war:

String^ ReduceWhitespace( String^ text )
{
  String^ newText;
  bool    inWhitespace = false;
  Int32   posStart = 0;
  Int32   pos      = 0;
  for( pos = 0; pos < text->Length; ++pos )
  {
    wchar_t cc = text[pos];
    if( Char::IsWhiteSpace( cc ) )
    {
      if( !inWhitespace )
      {
        if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
        inWhitespace = true;
        newText += L' ';
      }
      posStart = pos + 1;
    }
    else
    {
      if( inWhitespace )
      {
        inWhitespace = false;
        posStart = pos;
      }
    }
  }

  if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );

  return( newText );
}

Ich habe die obige Routine zuerst versucht, indem ich jedes Zeichen separat ersetzt habe, musste aber auf Teilzeichenfolgen für die Nicht-Leerzeichen-Abschnitte umsteigen. Bei der Anwendung auf eine 1.200.000 Zeichenfolge:

  • Die obige Routine erledigt dies in 25 Sekunden
  • die obige Routine + separate Zeichenersetzung in 95 Sekunden
  • Der Regex wurde nach 15 Minuten abgebrochen.
hvanbrug
quelle