Skuteczny sposób na usunięcie wszystkich białych znaków z łańcucha znaków?

Wywołuję REST API i odbieram odpowiedź XML z powrotem. Zwraca listę nazw obszaru roboczego, a ja piszę szybką metodę IsExistingWorkspace(). Ponieważ wszystkie przestrzenie robocze składają się z ciągów znaków bez białych znaków, zakładam, że najprostszym sposobem na sprawdzenie, czy dany obszar roboczy znajduje się na liście, jest usunięcie wszystkich białych znaków (w tym nowych linii) i wykonanie tego (XML jest ciągiem otrzymanym z żądania sieci web):

XML.Contains("<name>" + workspaceName + "</name>");
Wiem, że to rozróżnia wielkość liter i na tym polegam. Ja tylko potrzebujesz sposobu, aby skutecznie usunąć wszystkie białe znaki w ciągu znaków. Wiem, że RegEx i LINQ mogą to zrobić, ale jestem otwarty na inne pomysły. Głównie chodzi mi o szybkość.
Author: Peter Mortensen, 2011-06-02

16 answers

To najszybszy sposób, jaki znam, mimo że powiedziałeś, że nie chcesz używać wyrażeń regularnych:

Regex.Replace(XML, @"\s+", "")
 474
Author: slandau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 13:23:56

Mam alternatywny sposób bez wyrażenia regularnego i wydaje się, że działa całkiem dobrze. Jest to kontynuacja odpowiedzi Brandona Moretza:

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

Przetestowałem to w prostym teście jednostkowym:

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

Dla 1 000 000 prób pierwsza opcja (bez wyrażenia regularnego) uruchamia się w mniej niż sekundę (700 ms na moim komputerze), a druga zajmuje 3,5 sekundy.

 142
Author: Henk J Meulekamp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 13:27:10

Wypróbuj metodę replace napisu w C#.

XML.Replace(" ", string.Empty);
 66
Author: Mike_K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 21:47:04

Moim rozwiązaniem jest użycie Split i Join i jest to zaskakująco szybkie, w rzeczywistości najszybsze z najlepszych odpowiedzi tutaj.

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

Timingi dla 10 000 pętli na prostym łańcuchu z białymi spacjami i nowymi liniami i tabulatorami

  • split / join = 60 milisekund
  • LINQ chararray = 94 milisekundy
  • regex = 437 milisekund

Popraw to poprzez owinięcie go w method, aby nadać mu znaczenie, a także uczyń z niego metodę rozszerzenia, gdy przy niej jesteśmy ...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
 52
Author: kernowcode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-07 08:46:49

Bazując na Henks answer stworzyłem kilka metod testowych z jego odpowiedzią i kilka dodanych, bardziej zoptymalizowanych metod. Znalazłem wyniki różnią się w zależności od wielkości ciągu wejściowego. Dlatego testowałem z dwoma zestawami wyników. W najszybszej metodzie połączone źródło ma jeszcze szybszy sposób. Ale, ponieważ jest scharakteryzowany jako niebezpieczny, pominąłem to.

Long input string results:

  1. Inplacechararay: 2021 ms ( Sunsetquest ' s answer ) - (Original source )
  2. String reader: 6082 ms
  3. LINQ using native char.Iswhitespace: 7357 ms
  4. LINQ: 7746 ms (odpowiedź Henka )
  5. forloop: 32320 ms
  6. RegexCompiled: 37157 ms
  7. Regex: 42940 ms

Wyniki krótkiego ciągu wejściowego:

  1. Inplacechararay: 108 ms (odpowiedź Sunsetquest) - (oryginalne źródło )
  2. String reader: 327 ms
  3. ForLoop: 343 ms
  4. LINQ using native char.Iswhitespace: 624 ms
  5. LINQ: 645MS (odpowiedź Henka )
  6. RegexCompiled: 1671 ms
  7. Regex: 2599 ms

Kod :

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

Testy :

[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);
    }
}
 24
Author: Stian Standahl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 14:19:13

Tylko alternatywa, bo wygląda całkiem ładnie :) - Uwaga: odpowiedź Henksa jest najszybsza z nich.

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

Testowanie 1 000 000 pętli na "This is a simple Test"

Ta metoda = 1,74 sekundy
Regex = 2.58 sekundy
new String (Henks) = 0.82

 22
Author: BlueChippy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:31

Jeśli potrzebujesz doskonałej wydajności, powinieneś unikać LINQ i wyrażeń regularnych w tym przypadku. Zrobiłem kilka benchmarking wydajności, i wydaje się, że jeśli chcesz usunąć białe spacje od początku i końca łańcucha, ciąg.Trim() to twoja ostateczna funkcja.

Jeśli chcesz usunąć wszystkie białe spacje z ciągu znaków, poniższa metoda działa najszybciej ze wszystkich, które zostały tutaj opublikowane:

    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);
    }
 13
Author: JHM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 13:31:13

Znalazłem ładny zapis na ten na CodeProject Felipe Machado (z pomocą Richard Robertson )

Przetestował dziesięć różnych metod. Ten jest najszybszy unsafe wersja...
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));
}

I najszybszy Bezpieczny wersja...

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

Istnieją również fajne niezależne benchmarki na Stianie Standahlu, które pokazują również, że funkcja Felipe jest o około 300% szybsza od następnej najszybsza funkcja.

 13
Author: Sunsetquest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 14:22:00

Regex jest overkill; wystarczy użyć rozszerzenia na łańcuchu (dzięki Henk). Jest to banalne i powinno być częścią ram. Tak czy siak, oto moja realizacja:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}
 6
Author: Maksood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 14:15:24

Musiałem zastąpić białe spacje w łańcuchu spacjami, ale nie powielać spacji. na przykład, musiałem przekonwertować coś takiego jak:

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

Do

"a b c d e"

Zastosowałem następującą metodę

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();
}
 3
Author: user1325543,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-05 00:42:23

Oto prosta liniowa alternatywa dla rozwiązania RegEx. Nie jestem pewien, który jest szybszy; musiałbyś to porównać.

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();
}
 3
Author: Brandon Moretz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 13:25:32

Zakładam, że Twoja odpowiedź XML wygląda tak:

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

Najlepszym sposobem przetwarzania XML jest użycie parsera XML, takiego jak LINQ do XML :

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");
 2
Author: dtb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-02 19:40:28

Oto kolejny wariant:

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

Podobnie jak w przypadku większości innych rozwiązań, nie przeprowadziłem wyczerpujących testów porównawczych, ale działa to wystarczająco dobrze dla moich celów.

 1
Author: Fred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-18 19:49:43

Okazało się, że różne wyniki są prawdziwe. Próbuję zastąpić wszystkie białe spacje pojedynczą spacją, a wyrażenia regularne były bardzo powolne.

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

Dla mnie najbardziej optymalne (W C++ cli) było:

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

Próbowałem najpierw powyższej procedury, zastępując każdy znak osobno, ale musiałem przełączyć się na wykonywanie podciągów dla sekcji nie-spacji. Przy zastosowaniu do ciągu znaków 1,200,000:

  • powyższa procedura robi to w 25 sekund
  • the powyżej rutyna + oddzielna wymiana znaków w 95 sekund
  • regex został przerwany po 15 minutach.
 0
Author: hvanbrug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-04 21:01:02

Możemy użyć:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }
 0
Author: Tarik BENARAB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 14:59:52

Możemy użyć systemu.Linq i możemy to zrobić w jednej linijce:

string text = "My text with white spaces...";
text = new string(text.ToList().Where(c => c != ' ').ToArray());
 -2
Author: Pablo Caballero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-21 15:23:57