So ersetzen Sie mehrere Leerzeichen durch ein Leerzeichen

108

Angenommen, ich habe eine Zeichenfolge wie:

"Hello     how are   you           doing?"

Ich möchte eine Funktion, die mehrere Räume in einen Raum verwandelt.

Also würde ich bekommen:

"Hello how are you doing?"

Ich weiß, ich könnte Regex verwenden oder anrufen

string s = "Hello     how are   you           doing?".replace("  "," ");

Aber ich müsste es mehrmals aufrufen, um sicherzustellen, dass alle sequentiellen Leerzeichen durch nur eines ersetzt werden.

Gibt es dafür bereits eine eingebaute Methode?

Matt
quelle
Könnten Sie klarstellen: Beschäftigen Sie sich nur mit Leerzeichen oder "allen" Leerzeichen?
Jon Skeet
Und möchten Sie, dass Leerzeichen, die keine Leerzeichen sind, in Leerzeichen umgewandelt werden?
Jon Skeet
Ich meinte nur, dass alle Leerzeichen in Serie höchstens 1 sein sollten
Matt
1
Mögliches Duplikat von stackoverflow.com/questions/206717/…
Michael Freidgeim
2 Dinge zu beachten: 1. char.IsWhiteSpace enthält Wagenrücklauf, Zeilenvorschub usw. 2. 'Whitespace' wird wahrscheinlich genauer mit Char.GetUnicodeCategory (ch) = Globalization.UnicodeCategory.SpaceSeparator
smirkingman

Antworten:

195
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
Tim Hoolihan
quelle
40
Imo, Regex zu vermeiden, wenn Sie mit ihnen vertraut sind, ist vorzeitige Optimierung
Tim Hoolihan
8
Wenn Ihre Anwendung nicht zeitkritisch ist, kann sie sich 1 Mikrosekunde Verarbeitungsaufwand leisten.
Daniel
16
Beachten Sie, dass '\ s' nicht nur Leerzeichen, sondern auch neue Zeilenzeichen ersetzt.
Bart Kiers
12
Guter Fang, wenn Sie nur Leerzeichen möchten, wechseln Sie das Muster zu "[] +"
Tim Hoolihan
9
Sollten Sie nicht '{2,}' anstelle von '+' verwenden, um das Ersetzen einzelner Leerzeichen zu vermeiden?
Angularsen
52

Diese Frage ist nicht so einfach, wie andere Poster es sich vorgestellt haben (und wie ich ursprünglich geglaubt habe) - weil die Frage nicht ganz so präzise ist, wie sie sein muss.

Es gibt einen Unterschied zwischen "Leerzeichen" und "Leerzeichen". Wenn Sie nur Leerzeichen meinen, sollten Sie einen regulären Ausdruck von verwenden " {2,}". Wenn Sie meinen , alle Leerzeichen, das ist eine andere Sache. Sollten alle Leerzeichen in Leerzeichen umgewandelt werden? Was soll am Anfang und am Ende mit dem Weltraum passieren?

Für den unten stehenden Benchmark habe ich angenommen, dass Sie sich nur um Leerzeichen kümmern und auch am Anfang und am Ende nichts mit einzelnen Leerzeichen tun möchten.

Beachten Sie, dass Korrektheit fast immer wichtiger ist als Leistung. Die Tatsache, dass die Split / Join-Lösung alle führenden / nachfolgenden Leerzeichen (auch nur einzelne Leerzeichen) entfernt, ist in Bezug auf Ihre angegebenen Anforderungen falsch (was natürlich unvollständig sein kann).

Der Benchmark verwendet MiniBench .

using System;
using System.Text.RegularExpressions;
using MiniBench;

internal class Program
{
    public static void Main(string[] args)
    {

        int size = int.Parse(args[0]);
        int gapBetweenExtraSpaces = int.Parse(args[1]);

        char[] chars = new char[size];
        for (int i=0; i < size/2; i += 2)
        {
            // Make sure there actually *is* something to do
            chars[i*2] = (i % gapBetweenExtraSpaces == 1) ? ' ' : 'x';
            chars[i*2 + 1] = ' ';
        }
        // Just to make sure we don't have a \0 at the end
        // for odd sizes
        chars[chars.Length-1] = 'y';

        string bigString = new string(chars);
        // Assume that one form works :)
        string normalized = NormalizeWithSplitAndJoin(bigString);


        var suite = new TestSuite<string, string>("Normalize")
            .Plus(NormalizeWithSplitAndJoin)
            .Plus(NormalizeWithRegex)
            .RunTests(bigString, normalized);

        suite.Display(ResultColumns.All, suite.FindBest());
    }

    private static readonly Regex MultipleSpaces = 
        new Regex(@" {2,}", RegexOptions.Compiled);

    static string NormalizeWithRegex(string input)
    {
        return MultipleSpaces.Replace(input, " ");
    }

    // Guessing as the post doesn't specify what to use
    private static readonly char[] Whitespace =
        new char[] { ' ' };

    static string NormalizeWithSplitAndJoin(string input)
    {
        string[] split = input.Split
            (Whitespace, StringSplitOptions.RemoveEmptyEntries);
        return string.Join(" ", split);
    }
}

Einige Testläufe:

c:\Users\Jon\Test>test 1000 50
============ Normalize ============
NormalizeWithSplitAndJoin  1159091 0:30.258 22.93
NormalizeWithRegex        26378882 0:30.025  1.00

c:\Users\Jon\Test>test 1000 5
============ Normalize ============
NormalizeWithSplitAndJoin  947540 0:30.013 1.07
NormalizeWithRegex        1003862 0:29.610 1.00


c:\Users\Jon\Test>test 1000 1001
============ Normalize ============
NormalizeWithSplitAndJoin  1156299 0:29.898 21.99
NormalizeWithRegex        23243802 0:27.335  1.00

Hier ist die erste Zahl die Anzahl der Iterationen, die zweite die benötigte Zeit und die dritte eine skalierte Punktzahl, wobei 1,0 die beste ist.

Dies zeigt, dass zumindest in einigen Fällen (einschließlich diesem) ein regulärer Ausdruck die Split / Join-Lösung manchmal mit einem sehr signifikanten Vorsprung übertreffen kann .

Wenn Sie jedoch zu einer Anforderung "Alle Leerzeichen" wechseln, scheint Split / Join zu gewinnen. Wie so oft steckt der Teufel im Detail ...

Jon Skeet
quelle
1
Tolle Analyse. Es scheint also, dass wir beide in unterschiedlichem Maße korrekt waren. Der Code in meiner Antwort stammt aus einer größeren Funktion, mit der alle Leerzeichen und / oder Steuerzeichen innerhalb einer Zeichenfolge sowie von Anfang und Ende normalisiert werden können.
Scott Dorman
1
Mit nur den von Ihnen angegebenen Leerzeichen waren in den meisten meiner Tests Regex und Split / Join ungefähr gleich - S / J hatte einen winzigen Vorteil auf Kosten der Korrektheit und Komplexität. Aus diesen Gründen würde ich normalerweise den regulären Ausdruck bevorzugen. Versteh mich nicht falsch - ich bin weit entfernt von einem Regex-Fan, aber ich mag es nicht, aus Gründen der Leistung komplexeren Code zu schreiben, ohne die Leistung zuerst wirklich zu testen.
Jon Skeet
NormalizeWithSplitAndJoin erzeugt viel mehr Müll. Es ist schwer zu sagen, ob ein echtes Problem mehr GC-Zeit als das Banchmark hat.
Ian Ringrose
@IanRingrose Welche Art von Müll kann erzeugt werden?
Dronz
18

Ein reguläres Expressoin wäre der einfachste Weg. Wenn Sie die Regex richtig schreiben, benötigen Sie nicht mehrere Anrufe.

Ändern Sie es in dieses:

string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " "); 
Brandon
quelle
Mein einziges Problem @"\s{2,}"ist, dass einzelne Registerkarten und andere Unicode-Leerzeichen nicht durch Leerzeichen ersetzt werden können. Wenn Sie 2 Registerkarten durch ein Leerzeichen ersetzen möchten, sollten Sie wahrscheinlich 1 Registerkarte durch ein Leerzeichen ersetzen. @"\s+"werde das für dich tun.
David Specht
17

Obwohl die vorhandenen Antworten in Ordnung sind, möchte ich auf einen Ansatz hinweisen, der nicht funktioniert:

public static string DontUseThisToCollapseSpaces(string text)
{
    while (text.IndexOf("  ") != -1)
    {
        text = text.Replace("  ", " ");
    }
    return text;
}

Dies kann für immer wiederholt werden. Möchte jemand raten warum? (Ich bin erst darauf gestoßen, als es vor ein paar Jahren als Newsgroup-Frage gestellt wurde ... jemand ist tatsächlich als Problem darauf gestoßen.)

Jon Skeet
quelle
Ich denke, ich erinnere mich, dass diese Frage vor einiger Zeit auf SO gestellt wurde. IndexOf ignoriert bestimmte Zeichen, die Replace nicht ignoriert. Der doppelte Raum war also immer da, nur nie entfernt.
Brandon
19
Dies liegt daran, dass IndexOf einige Unicode-Zeichen ignoriert, wobei der spezifische Schuldige in diesem Fall ein asiatisches Zeichen iirc ist. Hmm, Nicht-Joiner mit einer Breite von Null laut Google.
Ahawker
Ich habe das auf die harte Tour
Antonio Bakula
Ich habe es auf die harte Tour gelernt. Insbesondere mit zwei Nicht-Joinern mit einer Breite von Null (\ u200C \ u200C). IndexOf gibt den Index dieses "doppelten Leerzeichens" zurück, aber Ersetzen ersetzt ihn nicht. Ich denke, das liegt daran, dass Sie für IndexOf StringComparsion (Ordinal) angeben müssen, damit es sich wie Replace verhält. Auf diese Weise findet keiner dieser beiden "doppelte Leerzeichen". Weitere Informationen zu StringComparsion docs.microsoft.com/en-us/dotnet/api/…
Martin Brabec
4

Wie bereits erwähnt, kann dies leicht durch einen regulären Ausdruck erreicht werden. Ich füge nur hinzu, dass Sie möglicherweise eine .trim () hinzufügen möchten, um führende / nachfolgende Leerzeichen zu entfernen.

MAK
quelle
4

Hier ist die Lösung, mit der ich arbeite. Ohne RegEx und String.Split.

public static string TrimWhiteSpace(this string Value)
{
    StringBuilder sbOut = new StringBuilder();
    if (!string.IsNullOrEmpty(Value))
    {
        bool IsWhiteSpace = false;
        for (int i = 0; i < Value.Length; i++)
        {
            if (char.IsWhiteSpace(Value[i])) //Comparion with WhiteSpace
            {
                if (!IsWhiteSpace) //Comparison with previous Char
                {
                    sbOut.Append(Value[i]);
                    IsWhiteSpace = true;
                }
            }
            else
            {
                IsWhiteSpace = false;
                sbOut.Append(Value[i]);
            }
        }
    }
    return sbOut.ToString();
}

also kannst du:

string cleanedString = dirtyString.TrimWhiteSpace();
Fubo
quelle
4

Ein schneller zusätzlicher Whitespace-Entferner ... Dies ist der schnellste und basiert auf Felipe Machados In-Place-Kopie.

static string InPlaceCharArray(string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false;
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        if (src[i] == '\u0020')
        {
            if (lastWasWS == false)
            {
                src[dstIdx++] = ch;
                lastWasWS = true;
            }
        }
        else
        { 
            lastWasWS = false;
            src[dstIdx++] = ch;
        }
    }
    return new string(src, 0, dstIdx);
}

Die Benchmarks ...

InPlaceCharArraySpaceOnly von Felipe Machado auf CodeProject 2015 und modifiziert von Sunsetquest für das Entfernen mehrerer Leerzeichen. Zeit: 3,75 Zecken

InPlaceCharArray von Felipe Machado 2015 und leicht modifiziert von Sunsetquest zum Entfernen mehrerer Räume. Zeit 6.50 Ticks (unterstützt auch Tabs)

SplitAndJoinOnSpace von Jon Skeet . Zeit: 13,25 Zecken

StringBuilder von fubo Zeit: 13,5 Ticks (unterstützt auch Tabs)

Regex mit Kompilierung von Jon Skeet . Zeit: 17 Zecken

StringBuilder von David S 2013 Zeit: 30,5 Ticks

Regex mit Nichtkompilierung von Brandon Zeit: 63,25 Ticks

StringBuilder von user214147 Zeit: 77.125 Ticks

Regex mit nicht kompiliertem Tim Hoolihan Zeit: 147,25 Ticks

Der Benchmark-Code ...

using System;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using System.Text;

static class Program
{
    public static void Main(string[] args)
    {
    long seed = ConfigProgramForBenchmarking();

    Stopwatch sw = new Stopwatch();

    string warmup = "This is   a Warm  up function for best   benchmark results." + seed;
    string input1 = "Hello World,    how are   you           doing?" + seed;
    string input2 = "It\twas\t \tso    nice  to\t\t see you \tin 1950.  \t" + seed;
    string correctOutput1 = "Hello World, how are you doing?" + seed;
    string correctOutput2 = "It\twas\tso nice to\tsee you in 1950. " + seed;
    string output1,output2;

    //warm-up timer function
    sw.Restart();
    sw.Stop();

    sw.Restart();
    sw.Stop();
    long baseVal = sw.ElapsedTicks;

    // InPlace Replace by Felipe Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
    output1 = InPlaceCharArraySpaceOnly (warmup);
    sw.Restart();
    output1 = InPlaceCharArraySpaceOnly (input1);
    output2 = InPlaceCharArraySpaceOnly (input2);
    sw.Stop();
    Console.WriteLine("InPlaceCharArraySpaceOnly : " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    // InPlace Replace by Felipe R. Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
    output1 = InPlaceCharArray(warmup);
    sw.Restart();
    output1 = InPlaceCharArray(input1);
    output2 = InPlaceCharArray(input2);
    sw.Stop();
    Console.WriteLine("InPlaceCharArray: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex with non-compile Tim Hoolihan (https://stackoverflow.com/a/1279874/2352507)
    string cleanedString = 
    output1 = Regex.Replace(warmup, @"\s+", " ");
    sw.Restart();
    output1 = Regex.Replace(input1, @"\s+", " ");
    output2 = Regex.Replace(input2, @"\s+", " ");
    sw.Stop();
    Console.WriteLine("Regex by Tim Hoolihan: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex with compile by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
    output1 = MultipleSpaces.Replace(warmup, " ");
    sw.Restart();
    output1 = MultipleSpaces.Replace(input1, " ");
    output2 = MultipleSpaces.Replace(input2, " ");
    sw.Stop();
    Console.WriteLine("Regex with compile by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
    output1 = SplitAndJoinOnSpace(warmup);
    sw.Restart();
    output1 = SplitAndJoinOnSpace(input1);
    output2 = SplitAndJoinOnSpace(input2);
    sw.Stop();
    Console.WriteLine("Split And Join by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex by Brandon (https://stackoverflow.com/a/1279878/2352507
    output1 = Regex.Replace(warmup, @"\s{2,}", " ");
    sw.Restart();
    output1 = Regex.Replace(input1, @"\s{2,}", " ");
    output2 = Regex.Replace(input2, @"\s{2,}", " ");
    sw.Stop();
    Console.WriteLine("Regex by Brandon: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
    output1 = user214147(warmup);
    sw.Restart();
    output1 = user214147(input1);
    output2 = user214147(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder by user214147: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
    output1 = fubo(warmup);
    sw.Restart();
    output1 = fubo(input1);
    output2 = fubo(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder by fubo: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));


    //StringBuilder by David S 2013 (https://stackoverflow.com/a/16035044/2352507)
    output1 = SingleSpacedTrim(warmup);
    sw.Restart();
    output1 = SingleSpacedTrim(input1);
    output2 = SingleSpacedTrim(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder(SingleSpacedTrim) by David S: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
}

// InPlace Replace by Felipe Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArray(string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false;
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        if (src[i] == '\u0020')
        {
            if (lastWasWS == false)
            {
                src[dstIdx++] = ch;
                lastWasWS = true;
            }
        }
        else
        { 
            lastWasWS = false;
            src[dstIdx++] = ch;
        }
    }
    return new string(src, 0, dstIdx);
}

// InPlace Replace by Felipe R. Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArraySpaceOnly (string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false; //Added line
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        switch (ch)
        {
            case '\u0020': //SPACE
            case '\u00A0': //NO-BREAK SPACE
            case '\u1680': //OGHAM SPACE MARK
            case '\u2000': // EN QUAD
            case '\u2001': //EM QUAD
            case '\u2002': //EN SPACE
            case '\u2003': //EM SPACE
            case '\u2004': //THREE-PER-EM SPACE
            case '\u2005': //FOUR-PER-EM SPACE
            case '\u2006': //SIX-PER-EM SPACE
            case '\u2007': //FIGURE SPACE
            case '\u2008': //PUNCTUATION SPACE
            case '\u2009': //THIN SPACE
            case '\u200A': //HAIR SPACE
            case '\u202F': //NARROW NO-BREAK SPACE
            case '\u205F': //MEDIUM MATHEMATICAL SPACE
            case '\u3000': //IDEOGRAPHIC SPACE
            case '\u2028': //LINE SEPARATOR
            case '\u2029': //PARAGRAPH SEPARATOR
            case '\u0009': //[ASCII Tab]
            case '\u000A': //[ASCII Line Feed]
            case '\u000B': //[ASCII Vertical Tab]
            case '\u000C': //[ASCII Form Feed]
            case '\u000D': //[ASCII Carriage Return]
            case '\u0085': //NEXT LINE
                if (lastWasWS == false) //Added line
                {
                    src[dstIdx++] = ch; //Added line
                    lastWasWS = true; //Added line
                }
            continue;
            default:
                lastWasWS = false; //Added line 
                src[dstIdx++] = ch;
                break;
        }
    }
    return new string(src, 0, dstIdx);
}

static readonly Regex MultipleSpaces =
    new Regex(@" {2,}", RegexOptions.Compiled);

//Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
static string SplitAndJoinOnSpace(string input)
{
    string[] split = input.Split(new char[] { ' '}, StringSplitOptions.RemoveEmptyEntries);
    return string.Join(" ", split);
}

//StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
public static string user214147(string S)
{
    string s = S.Trim();
    bool iswhite = false;
    int iwhite;
    int sLength = s.Length;
    StringBuilder sb = new StringBuilder(sLength);
    foreach (char c in s.ToCharArray())
    {
        if (Char.IsWhiteSpace(c))
        {
            if (iswhite)
            {
                //Continuing whitespace ignore it.
                continue;
            }
            else
            {
                //New WhiteSpace

                //Replace whitespace with a single space.
                sb.Append(" ");
                //Set iswhite to True and any following whitespace will be ignored
                iswhite = true;
            }
        }
        else
        {
            sb.Append(c.ToString());
            //reset iswhitespace to false
            iswhite = false;
        }
    }
    return sb.ToString();
}

//StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
public static string fubo(this string Value)
{
    StringBuilder sbOut = new StringBuilder();
    if (!string.IsNullOrEmpty(Value))
    {
        bool IsWhiteSpace = false;
        for (int i = 0; i < Value.Length; i++)
        {
            if (char.IsWhiteSpace(Value[i])) //Comparison with WhiteSpace
            {
                if (!IsWhiteSpace) //Comparison with previous Char
                {
                    sbOut.Append(Value[i]);
                    IsWhiteSpace = true;
                }
            }
            else
            {
                IsWhiteSpace = false;
                sbOut.Append(Value[i]);
            }
        }
    }
    return sbOut.ToString();
}

//David S. 2013 (https://stackoverflow.com/a/16035044/2352507)
public static String SingleSpacedTrim(String inString)
{
    StringBuilder sb = new StringBuilder();
    Boolean inBlanks = false;
    foreach (Char c in inString)
    {
        switch (c)
        {
            case '\r':
            case '\n':
            case '\t':
            case ' ':
                if (!inBlanks)
                {
                    inBlanks = true;
                    sb.Append(' ');
                }
                continue;
            default:
                inBlanks = false;
                sb.Append(c);
                break;
        }
    }
    return sb.ToString().Trim();
}

/// <summary>
/// We want to run this item with max priory to lower the odds of
/// the OS from doing program context switches in the middle of our code. 
/// source:https://stackoverflow.com/a/16157458 
/// </summary>
/// <returns>random seed</returns>
private static long ConfigProgramForBenchmarking()
{
    //prevent the JIT Compiler from optimizing Fkt calls away
    long seed = Environment.TickCount;
    //use the second Core/Processor for the test
    Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
    //prevent "Normal" Processes from interrupting Threads
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
    //prevent "Normal" Threads from interrupting this thread
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    return seed;
}

}}

Benchmark-Hinweise: Release-Modus, kein Debugger angeschlossen, i7-Prozessor, durchschnittlich 4 Läufe, nur kurze getestete Zeichenfolgen

Sunsetquest
quelle
1
Schön zu sehen, dass mein Artikel hier referenziert ist! (Ich bin Felipe Machado) Ich werde es mit einem geeigneten Benchmark-Tool namens BenchmarkDotNet aktualisieren! Ich werde versuchen, Läufe in allen Laufzeiten einzurichten (jetzt, wo wir DOT NET CORE und dergleichen haben ...
Loudenvier
1
@ Loudenvier - Gute Arbeit daran. Ihre war mit fast 400% die schnellste! .Net Core ist wie eine kostenlose Leistungssteigerung von 150-200%. Es nähert sich der C ++ - Leistung, ist aber viel einfacher zu codieren. Danke für den Kommentar.
Sunsetquest
Dies gilt nur für Leerzeichen, nicht für andere Leerzeichen. Vielleicht möchten Sie char.IsWhiteSpace (ch) anstelle von src [i] == '\ u0020'. Ich stelle fest, dass dies von der Community bearbeitet wurde. Haben sie es vermasselt?
Böse Taube
3

Ich teile, was ich benutze, weil ich anscheinend etwas anderes gefunden habe. Ich benutze das schon eine Weile und es ist schnell genug für mich. Ich bin mir nicht sicher, wie es sich gegen die anderen schlägt. Ich verwende es in einem begrenzten Dateischreiber und führe große Datentabellen feldweise durch.

    public static string NormalizeWhiteSpace(string S)
    {
        string s = S.Trim();
        bool iswhite = false;
        int iwhite;
        int sLength = s.Length;
        StringBuilder sb = new StringBuilder(sLength);
        foreach(char c in s.ToCharArray())
        {
            if(Char.IsWhiteSpace(c))
            {
                if (iswhite)
                {
                    //Continuing whitespace ignore it.
                    continue;
                }
                else
                {
                    //New WhiteSpace

                    //Replace whitespace with a single space.
                    sb.Append(" ");
                    //Set iswhite to True and any following whitespace will be ignored
                    iswhite = true;
                }  
            }
            else
            {
                sb.Append(c.ToString());
                //reset iswhitespace to false
                iswhite = false;
            }
        }
        return sb.ToString();
    }
user214147
quelle
2

Mit dem von Jon Skeet veröffentlichten Testprogramm habe ich versucht zu prüfen, ob eine handgeschriebene Schleife schneller ausgeführt werden kann.
Ich kann NormalizeWithSplitAndJoin jedes Mal schlagen, aber nur NormalizeWithRegex mit Eingaben von 1000, 5 schlagen.

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

    char lastChar = '*';  // anything other then space 
    for (int i = 0; i < input.Length; i++)
    {
        char thisChar = input[i];
        if (!(lastChar == ' ' && thisChar == ' '))
            output.Append(thisChar);

        lastChar = thisChar;
    }

    return output.ToString();
}

Ich habe mir den Maschinencode, den der Jitter erzeugt, nicht angesehen, aber ich gehe davon aus, dass das Problem die Zeit ist, die der Aufruf von StringBuilder.Append () benötigt, und um es besser zu machen, müsste unsicherer Code verwendet werden.

Also ist Regex.Replace () sehr schnell und schwer zu schlagen !!

Ian Ringrose
quelle
2

VB.NET

Linha.Split(" ").ToList().Where(Function(x) x <> " ").ToArray

C #

Linha.Split(" ").ToList().Where(x => x != " ").ToArray();

Genießen Sie die Leistung von LINQ = D.

Patryk Moura
quelle
Genau! Für mich ist dies auch der eleganteste Ansatz. Also für die Aufzeichnung, in C # wäre das:string.Join(" ", myString.Split(' ').Where(s => s != " ").ToArray())
Efrain
1
Kleinere Verbesserung der Split, um alle Leerzeichen zu fangen und die WhereKlausel zu entfernen :myString.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)
David
1
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");
Michael D.
quelle
Dies ersetzt alle Nicht-Wort-Zeichen durch Leerzeichen. Es würde also auch Dinge wie Klammern und Anführungszeichen usw. ersetzen, die möglicherweise nicht Ihren Wünschen entsprechen.
Herman
0

Kleinste Lösung:

var regExp = / \ s + / g, newString = oldString.replace (regExp, '');


quelle
0

Sie können dies versuchen:

    /// <summary>
    /// Remove all extra spaces and tabs between words in the specified string!
    /// </summary>
    /// <param name="str">The specified string.</param>
    public static string RemoveExtraSpaces(string str)
    {
        str = str.Trim();
        StringBuilder sb = new StringBuilder();
        bool space = false;
        foreach (char c in str)
        {
            if (char.IsWhiteSpace(c) || c == (char)9) { space = true; }
            else { if (space) { sb.Append(' '); }; sb.Append(c); space = false; };
        }
        return sb.ToString();
    }
LL99
quelle
0

Ersatzgruppen bieten einen impliziten Ansatz, um das Ersetzen mehrerer Leerzeichen durch dasselbe zu lösen :

    public static void WhiteSpaceReduce()
    {
        string t1 = "a b   c d";
        string t2 = "a b\n\nc\nd";

        Regex whiteReduce = new Regex(@"(?<firstWS>\s)(?<repeatedWS>\k<firstWS>+)");
        Console.WriteLine("{0}", t1);
        //Console.WriteLine("{0}", whiteReduce.Replace(t1, x => x.Value.Substring(0, 1))); 
        Console.WriteLine("{0}", whiteReduce.Replace(t1, @"${firstWS}"));
        Console.WriteLine("\nNext example ---------");
        Console.WriteLine("{0}", t2);
        Console.WriteLine("{0}", whiteReduce.Replace(t2, @"${firstWS}"));
        Console.WriteLine();
    }

Bitte beachten Sie, dass das zweite Beispiel einfach bleibt, \nwährend die akzeptierte Antwort das Zeilenende durch Leerzeichen ersetzen würde.

Wenn Sie eine Kombination von Leerzeichen durch die erste ersetzen müssen , entfernen Sie einfach die Rückreferenz \kaus dem Muster.

Dan
quelle
-1

Es gibt keine Möglichkeit, dies zu tun. Sie können dies versuchen:

private static readonly char[] whitespace = new char[] { ' ', '\n', '\t', '\r', '\f', '\v' };
public static string Normalize(string source)
{
   return String.Join(" ", source.Split(whitespace, StringSplitOptions.RemoveEmptyEntries));
}

Dadurch werden führende und nachfolgende Leerzeichen entfernt und interne Leerzeichen zu einem einzelnen Leerzeichen zusammengefasst. Wenn Sie wirklich nur Leerzeichen reduzieren möchten, sind die Lösungen mit einem regulären Ausdruck besser. Ansonsten ist diese Lösung besser. (Siehe die Analyse von Jon Skeet.)

Scott Dorman
quelle
7
Wenn der reguläre Ausdruck kompiliert und zwischengespeichert wird, bin ich mir nicht sicher, ob dies mehr Aufwand bedeutet als das Teilen und Verbinden, was zu einer Menge von Zwischen-Garbage-Strings führen kann. Haben Sie beide Ansätze sorgfältig bewertet, bevor Sie davon ausgegangen sind, dass Ihr Weg schneller ist?
Jon Skeet
1
Leerzeichen ist hier nicht deklariert
Tim Hoolihan
3
Apropos Overhead, warum um alles in der Welt rufst du an source.ToCharArray()und wirfst dann das Ergebnis weg?
Jon Skeet
2
Und rufe ToCharArray()auf dem Ergebnis der string.Join, nur eine neue Zeichenfolge zu erstellen ... wow, denn das ist in einem Beitrag zu dem Kopf beschwert nur bemerkenswert ist. -1.
Jon Skeet
1
Oh, und unter der Annahme whitespaceist new char[] { ' ' }, wird dies das falsche Ergebnis , wenn die Eingabezeichenfolge beginnt oder endet mit einem Raum geben.
Jon Skeet