it-swarm.com.de

c # Der schnellste Weg, um zusätzliche Leerräume zu entfernen

Was ist der schnellste Weg, um zusätzliche Leerzeichen in ein Leerzeichen zu ersetzen?
z.B.

von  

foo      bar 

bis

foo bar
37
Navid Rahmani

Der schnellste Weg Durchlaufen Sie den String und erstellen Sie eine zweite Kopie in einer StringBuilder Zeichenweise, wobei Sie für jede Gruppe von Leerzeichen nur ein Leerzeichen kopieren.

Die einfacher zu Replace-Varianten generieren eine Eimerladung mit zusätzlichen Zeichenfolgen (oder verschwenden Zeit beim Erstellen des regulären DFA).

Mit Vergleichsergebnissen bearbeiten:

Mit http://ideone.com/h6pw3 , mit n = 50 (musste es auf Ideone reduzieren, da es so lange gedauert hat, dass sie meinen Prozess beenden mussten), bekomme ich:

Regex: 7771 ms. 

Stringbuilder: 894ms.

Was tatsächlich so ist wie erwartet, ist Regex für etwas so einfaches schrecklich ineffizient.

44
Blindy

Sie können einen Regex verwenden:

static readonly Regex trimmer = new Regex(@"\s\s+");

s = trimmer.Replace(s, " ");

Für zusätzliche Leistung übergeben Sie RegexOptions.Compiled.

38
SLaks

Ein bisschen spät, aber ich habe ein paar Benchmarks durchgeführt, um den schnellsten Weg zu finden, um zusätzliche Whitespaces zu entfernen. Wenn es schnellere Antworten gibt, würde ich sie gerne hinzufügen.

Ergebnisse:

  1. NormalizeWhiteSpaceForLoop: 156 ms ( von mir - Von meiner Antwort zum Entfernen aller Leerzeichen )
  2. NormalizeWhiteSpace: 267 ms ( von Alex K. )
  3. RegexCompiled: 1950 ms ( von SLaks )
  4. Regex: 2261 ms ( von SLaks )

Code:

public class RemoveExtraWhitespaces
{
    public static string WithRegex(string text)
    {
        return Regex.Replace(text, @"\s+", " ");
    }

    public static string WithRegexCompiled(Regex compiledRegex, string text)
    {
        return compiledRegex.Replace(text, " ");
    }

    public static string NormalizeWhiteSpace(string input)
    {
        if (string.IsNullOrEmpty(input))
            return string.Empty;

        int current = 0;
        char[] output = new char[input.Length];
        bool skipped = false;

        foreach (char c in input.ToCharArray())
        {
            if (char.IsWhiteSpace(c))
            {
                if (!skipped)
                {
                    if (current > 0)
                        output[current++] = ' ';

                    skipped = true;
                }
            }
            else
            {
                skipped = false;
                output[current++] = c;
            }
        }

        return new string(output, 0, current);
    }

    public static string NormalizeWhiteSpaceForLoop(string input)
    {
        int len = input.Length,
            index = 0,
            i = 0;
        var src = input.ToCharArray();
        bool skip = false;
        char ch;
        for (; i < len; i++)
        {
            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':
                    if (skip) continue;
                    src[index++] = ch;
                    skip = true;
                    continue;
                default:
                    skip = false;
                    src[index++] = ch;
                continue;
            }
        }

        return new string(src, 0, index);
    }
}

Tests:

[TestFixture]
public class RemoveExtraWhitespacesTest
{
    private const string _text = "foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo foo                  bar                  foobar                     moo ";
    private const string _expected = "foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo ";

    private const int _iterations = 10000;

    [Test]
    public void Regex()
    {
        var result = TimeAction("Regex", () => RemoveExtraWhitespaces.WithRegex(_text));
        Assert.AreEqual(_expected, result);
    }

    [Test]
    public void RegexCompiled()
    {
        var compiledRegex = new Regex(@"\s+", RegexOptions.Compiled);
        var result = TimeAction("RegexCompiled", () => RemoveExtraWhitespaces.WithRegexCompiled(compiledRegex, _text));
        Assert.AreEqual(_expected, result);
    }

    [Test]
    public void NormalizeWhiteSpace()
    {
        var result = TimeAction("NormalizeWhiteSpace", () => RemoveExtraWhitespaces.NormalizeWhiteSpace(_text));
        Assert.AreEqual(_expected, result);
    }

    [Test]
    public void NormalizeWhiteSpaceForLoop()
    {
        var result = TimeAction("NormalizeWhiteSpaceForLoop", () => RemoveExtraWhitespaces.NormalizeWhiteSpaceForLoop(_text));
        Assert.AreEqual(_expected, result);
    }

    public string TimeAction(string name, Func<string> func)
    {
        var timer = Stopwatch.StartNew();
        string result = string.Empty; ;
        for (int i = 0; i < _iterations; i++)
        {
            result = func();
        }

        timer.Stop();
        Console.WriteLine(string.Format("{0}: {1} ms", name, timer.ElapsedMilliseconds));
        return result;
    }
}
21
Stian Standahl

Ich verwende die folgenden Methoden - sie behandeln alle Whitespace-Zeichen nicht nur Leerzeichen, trimmen beide - führenden und nachlaufenden Whitespaces, entfernen zusätzliche Whitespaces und alle Whitespaces werden durch Leerzeichen ersetzt char (also haben wir ein einheitliches Leerzeichen). Und diese Methoden sind schnell .

public static String CompactWhitespaces( String s )
{
    StringBuilder sb = new StringBuilder( s );

    CompactWhitespaces( sb );

    return sb.ToString();
}

public static void CompactWhitespaces( StringBuilder sb )
{
    if( sb.Length == 0 )
        return;

    // set [start] to first not-whitespace char or to sb.Length

    int start = 0;

    while( start < sb.Length )
    {
        if( Char.IsWhiteSpace( sb[ start ] ) )
            start++;
        else 
            break;
    }

    // if [sb] has only whitespaces, then return empty string

    if( start == sb.Length )
    {
        sb.Length = 0;
        return;
    }

    // set [end] to last not-whitespace char

    int end = sb.Length - 1;

    while( end >= 0 )
    {
        if( Char.IsWhiteSpace( sb[ end ] ) )
            end--;
        else 
            break;
    }

    // compact string

    int dest = 0;
    bool previousIsWhitespace = false;

    for( int i = start; i <= end; i++ )
    {
        if( Char.IsWhiteSpace( sb[ i ] ) )
        {
            if( !previousIsWhitespace )
            {
                previousIsWhitespace = true;
                sb[ dest ] = ' ';
                dest++;
            }
        }
        else
        {
            previousIsWhitespace = false;
            sb[ dest ] = sb[ i ];
            dest++;
        }
    }

    sb.Length = dest;
}
11
string text = "foo       bar";
text = Regex.Replace(text, @"\s+", " ");
// text = "foo bar"

Diese Lösung funktioniert mit Leerzeichen, Registerkarten und Zeilenumbrüchen. Wenn Sie nur Leerzeichen wünschen, ersetzen Sie '\ s' durch ''.

6
OpticalDelusion
string q = " Hello     how are   you           doing?";
string a = String.Join(" ", q.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
6
Detlef Kroll

Ich brauchte eine davon für größere Saiten und stellte mir die Routine vor.

Alle aufeinanderfolgenden Leerzeichen (einschließlich Tabulatoren, Zeilenumbrüche) werden durch das ersetzt, was sich in normalizeTo..__ befindet. Führende/nachgestellte Leerzeichen werden entfernt.

Es ist etwa 8-mal schneller als ein RegEx mit meinen 5k-> 5mil-Zeichenketten.

internal static string NormalizeWhiteSpace(string input, char normalizeTo = ' ')
{
    if (string.IsNullOrEmpty(input))
        return string.Empty;

    int current = 0;
    char[] output = new char[input.Length];
    bool skipped = false;

    foreach (char c in input.ToCharArray())
    {
        if (char.IsWhiteSpace(c))
        {
            if (!skipped)
            {
                if (current > 0)
                    output[current++] = normalizeTo;

                skipped = true;
            }
        }
        else
        {
            skipped = false;
            output[current++] = c;
        }
    }

    return new string(output, 0, skipped ? current - 1 : current);
}
6
Alex K.
string yourWord = "beep boop    baap beep   boop    baap             beep";

yourWord = yourWord .Replace("  ", " |").Replace("| ", "").Replace("|", "");
5
Abu Dina

Ich habe versucht, StringBuilder zu verwenden:

  1. entfernen Sie zusätzliche Leerzeichen-Teilzeichenfolgen
  2. akzeptieren Sie Zeichen aus dem Schleifen der Originalzeichenfolge, wie Blindy vorschlägt

Hier ist die beste Balance zwischen Leistung und Lesbarkeit, die ich gefunden habe (mit 100.000 Iterations-Timing-Läufen). Manchmal testet dies schneller als eine weniger lesbare Version, höchstens 5% langsamer. Auf meiner kleinen Testzeichenfolge benötigt Regex 4.24x so viel Zeit.

public static string RemoveExtraWhitespace(string str)
    {
        var sb = new StringBuilder();
        var prevIsWhitespace = false;
        foreach (var ch in str)
        {
            var isWhitespace = char.IsWhiteSpace(ch);
            if (prevIsWhitespace && isWhitespace)
            {
                continue;
            }
            sb.Append(ch);
            prevIsWhitespace = isWhitespace;
        }
        return sb.ToString();
    }
4
TTT

Es ist nicht schnell, aber wenn Einfachheit hilft, funktioniert das:

while (text.Contains("  ")) text=text.Replace("  ", " ");
2
user3029478

Dieser Code funktioniert gut. Ich habe die Leistung nicht gemessen.

string text = "   hello    -  world,  here   we go  !!!    a  bc    ";
string.Join(" ", text.Split().Where(x => x != ""));
// Output
// "hello - world, here we go !!! a bc"
1

Einige Anforderungen sind in dieser Frage nicht klar, worüber man nachdenken sollte. 

  1. Möchten Sie einen einzelnen führenden oder nachgestellten Leerzeichen?
  2. Wenn Sie alle Leerzeichen durch ein einzelnes Zeichen ersetzen, möchten Sie, dass dieses Zeichen konsistent ist? (d. h. viele dieser Lösungen würden\t\t durch\t und '' durch '' ersetzen.

Dies ist eine sehr effiziente Version, die alle Leerzeichen durch ein einzelnes Leerzeichen ersetzt und alle führenden und nachfolgenden Leerzeichen vor der for-Schleife entfernt.

  public static string WhiteSpaceToSingleSpaces(string input)
  {
    if (input.Length < 2) 
        return input;

    StringBuilder sb = new StringBuilder();

    input = input.Trim();
    char lastChar = input[0];
    bool lastCharWhiteSpace = false;

    for (int i = 1; i < input.Length; i++)
    {
        bool whiteSpace = char.IsWhiteSpace(input[i]);

        //Skip duplicate whitespace characters
        if (whiteSpace && lastCharWhiteSpace)
            continue;

        //Replace all whitespace with a single space.
        if (whiteSpace)
            sb.Append(' ');
        else
            sb.Append(input[i]);

        //Keep track of the last character's whitespace status
        lastCharWhiteSpace = whiteSpace;
    }

    return sb.ToString();
  }
1
Jake Drew

Ich weiß nicht, ob es der schnellste Weg ist, aber ich benutze dies und das funktioniert für mich:

    /// <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();
    }
1
LL99

versuche dies:

System.Text.RegularExpressions.Regex.Replace(input, @"\s+", " ");
1
jaltiere
public string GetCorrectString(string IncorrectString)
    {
        string[] strarray = IncorrectString.Split(' ');
        var sb = new StringBuilder();
        foreach (var str in strarray)
        {
            if (str != string.Empty)
            {
                sb.Append(str).Append(' ');
            }
        }
        return sb.ToString().Trim();
    }
0
chandudab
public static string RemoveExtraSpaces(string input)
{
    input = input.Trim();
    string output = "";
    bool WasLastCharSpace = false;
    for (int i = 0; i < input.Length; i++)
    {
        if (input[i] == ' ' && WasLastCharSpace)
            continue;
        WasLastCharSpace = input[i] == ' ';
        output += input[i];
    }
    return output;
}
0
Saeed Taran

Für diejenigen, die nur kopieren und einfügen möchten:

    private string RemoveExcessiveWhitespace(string value)
    {
        if (value == null) { return null; }

        var builder = new StringBuilder();
        var ignoreWhitespace = false;
        foreach (var c in value)
        {
            if (!ignoreWhitespace || c != ' ')
            {
                builder.Append(c);
            }
            ignoreWhitespace = c == ' ';
        }
        return builder.ToString();
    }
0
Mert Akcakaya

Ich weiß, dass dies sehr alt ist, aber der einfachste Weg, Leerzeichen zu verdichten (ersetzen Sie alle wiederkehrenden Leerzeichen durch ein einzelnes Leerzeichen) ist wie folgt:

    public static string CompactWhitespace(string astring)
    {
        if (!string.IsNullOrEmpty(astring))
        {
            bool found = false;
            StringBuilder buff = new StringBuilder();

            foreach (char chr in astring.Trim())
            {
                if (char.IsWhiteSpace(chr))
                {
                    if (found)
                    {
                        continue;
                    }

                    found = true;
                    buff.Append(' ');
                }
                else
                {
                    if (found)
                    {
                        found = false;
                    }

                    buff.Append(chr);
                }
            }

            return buff.ToString();
        }

        return string.Empty;
    }
0
Brien Halstead

Ich bin mit C # nicht sehr vertraut, daher ist mein Code nicht elegant/effizient. Ich bin hierher gekommen, um eine Antwort zu finden, die zu meinem Anwendungsfall passt, aber ich konnte keine finden (oder ich konnte keine finden).

Für meinen Anwendungsfall musste ich alle White Spaces (WS: {space, tab, cr lf}) mit den folgenden Bedingungen normalisieren:

  • WS kann in beliebiger Kombination kommen
  • Ersetzen Sie eine Sequenz von WS durch das höchstwertige WS
  • tab muss in einigen Fällen beibehalten werden (z. B. eine tabulatorgetrennte Datei und in diesem Fall müssen auch wiederholte Tabs beibehalten werden). In den meisten Fällen müssen sie jedoch in Leerzeichen umgewandelt werden.

Hier also eine Beispieleingabe und eine erwartete Ausgabe (Haftungsausschluss: Mein Code ist nur für dieses Beispiel getestet).



        Every night    in my            dreams  I see you, I feel you
    That's how    I know you go on

Far across the  distance and            places between us   



You            have                 come                    to show you go on


umgewandelt werden in

Every night in my dreams I see you, I feel you
That's how I know you go on
Far across the distance and places between us
You have come to show you go on

Hier ist mein Code

using System;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main(string text)
    {
        bool preserveTabs = false;

        //[Step 1]: Clean up white spaces around the text
        text = text.Trim();
        //Console.Write("\nTrim\n======\n" + text);

        //[Step 2]: Reduce repeated spaces to single space. 
        text = Regex.Replace(text, @" +", " ");
        // Console.Write("\nNo repeated spaces\n======\n" + text);

        //[Step 3]: Hande Tab spaces. Tabs needs to treated with care because 
        //in some files tabs have special meaning (for eg Tab seperated files)
        if(preserveTabs)
        {
            text = Regex.Replace(text, @" *\t *", "\t");
        }
        else
        {
            text = Regex.Replace(text, @"[ \t]+", " ");
        }
        //Console.Write("\nTabs preserved\n======\n" + text);

        //[Step 4]: Reduce repeated new lines (and other white spaces around them)
                  //into a single new line.
        text = Regex.Replace(text, @"([\t ]*(\n)+[\t ]*)+", "\n");
        Console.Write("\nClean New Lines\n======\n" + text);    
    }
}

Sehen Sie diesen Code hier in Aktion: https://dotnetfiddle.net/eupjIU

0
BeNiza

Fehlt mir hier etwas? Ich kam dazu:

// Input: "HELLO     BEAUTIFUL       WORLD!"
private string NormalizeWhitespace(string inputStr)
{
    // First split the string on the spaces but exclude the spaces themselves
    // Using the input string the length of the array will be 3. If the spaces
    // were not filtered out they would be included in the array
    var splitParts = inputStr.Split(' ').Where(x => x != "").ToArray();

   // Now iterate over the parts in the array and add them to the return
   // string. If the current part is not the last part, add a space after.
   for (int i = 0; i < splitParts.Count(); i++)
   {
        retVal += splitParts[i];
        if (i != splitParts.Count() - 1)
        {
            retVal += " ";
        }
   }
    return retVal;
}
// Would return "HELLO BEAUTIFUL WORLD!"

Ich weiß, dass ich hier eine zweite Zeichenfolge erstellt, um sie zurückzugeben, sowie das splitParts-Array. Nur gedacht, das ist ziemlich geradlinig. Vielleicht berücksichtige ich einige der möglichen Szenarien nicht. 

0
Tom

sie können indexOf verwenden, um zuerst den Anfang der Whitespace-Sequenzen zu ermitteln, und dann die Methode replace zu verwenden, um den Leerraum in "" zu ändern. Von dort aus können Sie den Index verwenden, den Sie mitgenommen haben, und einen Whitespace-Charakter an dieser Stelle platzieren.

0
MGZero

Das ist lustig, aber auf meinem PC ist die unten beschriebene Methode genauso schnell wie der StringBulder-Ansatz von Sergey Povalyaev - (~ 282ms für 1000 Wiederholungen, 10k-Strings). Nicht sicher über die Speichernutzung.

string RemoveExtraWhiteSpace(string src, char[] wsChars){
   return string.Join(" ",src.Split(wsChars, StringSplitOptions.RemoveEmptyEntries));
}

Offensichtlich funktioniert es gut mit beliebigen Zeichen - nicht nur mit Leerzeichen.

Das ist zwar nicht das, wonach das OP gefragt hat - aber wenn Sie wirklich bestimmte aufeinanderfolgende Zeichen in einer Zeichenfolge durch nur eine Instanz ersetzen müssen, können Sie diese relativ effiziente Methode verwenden:

    string RemoveDuplicateChars(string src, char[] dupes){  
        var sd = (char[])dupes.Clone();  
        Array.Sort(sd);

        var res = new StringBuilder(src.Length);

        for(int i = 0; i<src.Length; i++){
            if( i==0 || src[i]!=src[i-1] || Array.BinarySearch(sd,src[i])<0){
                res.Append(src[i]); 
            }
        }
        return res.ToString();
    }
0
Zar Shardan

Ich habe es gerade aufgemischt, habe es aber noch nicht getestet. Aber ich fand das elegant und vermeidet Regex: 

    /// <summary>
    /// Removes extra white space.
    /// </summary>
    /// <param name="s">
    /// The string
    /// </param>
    /// <returns>
    /// The string, with only single white-space groupings. 
    /// </returns>
    public static string RemoveExtraWhiteSpace(this string s)
    {
        if (s.Length == 0)
        {
            return string.Empty;
        }

        var stringBuilder = new StringBuilder();
        var whiteSpaceCount = 0;
        foreach (var character in s)
        {
            if (char.IsWhiteSpace(character))
            {
                whiteSpaceCount++;
            }
            else
            {
                whiteSpaceCount = 0;
            }

            if (whiteSpaceCount > 1)
            {
                continue;
            }

            stringBuilder.Append(character);
        }

        return stringBuilder.ToString();
    }
0
Kris Coleman