Efficiënte manier om ALLE witruimte uit String te verwijderen?

Ik roep een REST API aan en ontvang een XML-antwoord terug. Het retourneert een lijst met namen van werkruimten en ik schrijf een snelle methode IsExistingWorkspace(). Aangezien alle werkruimten bestaan uit aaneengesloten tekens zonder witruimte, neem ik aan dat de gemakkelijkste manier om erachter te komen of een bepaalde werkruimte in de lijst staat, is om alle witruimte (inclusief nieuwe regels) te verwijderen en dit te doen (XML is de tekenreeks die is ontvangen van het web verzoek):

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

Ik weet dat het hoofdlettergevoelig is en daar vertrouw ik op. Ik heb alleen een manier nodig om alle witruimte in een string efficiënt te verwijderen. Ik weet dat RegEx en LINQ het kunnen, maar ik sta open voor andere ideeën. Ik maak me vooral zorgen over snelheid.


Antwoord 1, autoriteit 100%

Dit is de snelste manier die ik ken, ook al zei je dat je geen reguliere expressies wilde gebruiken:

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

Crediting @hypehuman in de opmerkingen, als u van plan bent dit meer dan eens te doen, maak dan een Regex-instantie en sla deze op. Dit bespaart de overhead van het elke keer opnieuw bouwen, wat duurder is dan je zou denken.

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

Antwoord 2, autoriteit 29%

Ik heb een alternatieve manier zonder regexp, en het lijkt redelijk goed te presteren. Het is een vervolg op het antwoord van Brandon Moretz:

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

Ik heb het getest in een eenvoudige eenheidstest:

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

Bij 1.000.000 pogingen wordt de eerste optie (zonder regexp) in minder dan een seconde uitgevoerd (700 ms op mijn computer), en de tweede duurt 3,5 seconden.


Antwoord 3, autoriteit 14%

Probeer de vervangmethode van de tekenreeks in C#.

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

Antwoord 4, autoriteit 13%

Mijn oplossing is om Split and Join te gebruiken en het is verrassend snel, in feite de snelste van de beste antwoorden hier.

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

Timingen voor 10.000 lussen op eenvoudige string met witruimte, inclusief nieuwe regels en tabbladen

  • splitsen/deelnemen = 60 milliseconden
  • linq chararray = 94 milliseconden
  • regex = 437 milliseconden

Verbeter dit door het in de methode in te wikkelen om het betekenis te geven en maakt het ook een uitbreidingsmethode terwijl we eraan zijn …

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

Antwoord 5, Autoriteit 8%

bouwen op HENKS ANTWOORD Ik heb een aantal testmethoden gemaakt met zijn antwoord en een aantal toegevoegd, meer geoptimaliseerde methoden. Ik vond de resultaten op basis van de grootte van de invoerreeks. Daarom heb ik getest met twee resultaat-sets. In de snelste methode heeft de gekoppelde bron een nog snellere manier. Maar omdat het wordt gekenmerkt als onveilig, heb ik dit achtergelaten.

Long Input String Resultaten:

  1. Inplacechararray: 2021 MS (Antwoord van SunsetQuest ) – (Originele bron )
  2. SPLIT SPLIFT EN JUISTERD: 4277MS (Kernowcode’s antwoord )
  3. string lezer: 6082 ms
  4. LINQ met behulp van native char.iswhitespace: 7357 ms
  5. LINQ: 7746 MS (Henk’s antwoord )
  6. FORLOOP: 32320 MS
  7. RegexCompileer: 37157 MS
  8. RGEX: 42940 MS

korte invoer string Resultaten:

  1. Inplacechararray: 108 MS (Antwoord van SunsetQuest ) – (Originele bron )
  2. String splitsen en aansluiten: 294 ms (Kernowcode’s antwoord)
  3. Stringlezer: 327 ms
  4. ForLoop: 343 ms
  5. LINQ met native char.IsWhitespace: 624 ms
  6. LINQ: 645ms (Henks antwoord)
  7. Regex gecompileerd: 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);
    }
}

Bewerken : getest een mooie voering van Kernowcode.


Antwoord 6, Autoriteit 4%

Gewoon een alternatief omdat het er best goed uitziet 🙂 – Opmerking: Henks Antwoord is de snelste van deze.

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

Testen van 1.000.000 lussen op "This is a simple Test"

deze methode = 1,74 seconden
Regex = 2,58 seconden
new String(HENKS) = 0,82 seconden


Antwoord 7, Autoriteit 3%

Ik vond een leuke schrijven -Up op deze op codeproject door Felipe Machado (met hulp door Richard Robertson )

Hij testte tien verschillende methoden. Deze is de snelste veilig -versie …

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

En de snelste onveiligeversie… (enkele verbeteringen door Sunsetquest 5/26/2021 )

public static unsafe void RemoveAllWhitespace(ref 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;
            }
        }
        uint* pi = (uint*)pfixed;
        ulong len = ((ulong)dst - (ulong)pfixed) >> 1;
        pi[-1] = (uint)len;
        pfixed[len] = '\0';
    }
}

Er zijn ook enkele mooie onafhankelijke benchmarksop Stack Overflow van Stian Standahl die ook laten zien hoe de functie van Felipe ongeveer 300% is sneller dan de volgende snelste functie. En voor degene die ik heb aangepast, heb ik dezetruc gebruikt.


Antwoord 8, autoriteit 2%

Als je uitstekende prestaties nodig hebt, moet je in dit geval LINQ en reguliere expressies vermijden. Ik heb wat prestatiebenchmarks gedaan en het lijkt erop dat als je witruimte aan het begin en einde van de string wilt verwijderen, string.Trim() je ultieme functie is.

Als je alle spaties van een string moet verwijderen, werkt de volgende methode het snelst van alles wat hier is gepost:

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

Antwoord 9, autoriteit 2%

Regex is overdreven; gebruik gewoon extensie op string (bedankt Henk). Dit is triviaal en had deel moeten uitmaken van het kader. Hoe dan ook, hier is mijn implementatie:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}

Antwoord 10

Hier is een eenvoudige lineaire alternatief voor de RegEx-oplossing. Ik ben niet zeker dat is sneller; je zou moeten benchmark.

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

Antwoord 11

ik nodig had om witte ruimte te vervangen in een string met spaties, maar de plaatsen niet dupliceren. bijv., ik nodig had om iets als het volgende te zetten:

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

Naar

"a b c d e"

I gebruikt de volgende methode

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

Antwoord 12

Ik neem aan dat uw XML-respons ziet er als volgt uit:

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

De beste manier om XML te verwerken is door een XML-parser te gebruiken, zoals LINQ to XML:

var doc = XDocument.Parse(xml);
var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");

Antwoord 13

We kunnen gebruiken:

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

Antwoord 14

Met Linq kun je op deze manier een leesbare methode schrijven:

   public static string RemoveAllWhitespaces(this string source)
    {
        return string.IsNullOrEmpty(source) ? source : new string(source.Where(x => !char.IsWhiteSpace(x)).ToArray());
    }

Antwoord 15

Hier is nog een andere variant:

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

Zoals bij de meeste andere oplossingen heb ik geen uitgebreide benchmarktests uitgevoerd, maar dit werkt goed genoeg voor mijn doeleinden.


Antwoord 16

Ik heb ontdekt dat verschillende resultaten waar zijn. Ik probeer alle witruimte te vervangen door een enkele spatie en de regex was extreem traag.

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

Wat voor mij het meest optimaal werkte (in C++ cli) was:

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

Ik heb de bovenstaande routine eerst geprobeerd door elk teken afzonderlijk te vervangen, maar moest overschakelen naar het doen van substrings voor de niet-spatiesecties. Bij toepassing op een tekenreeks van 1.200.000 tekens:

  • de bovenstaande routine doet het in 25 seconden
  • de bovenstaande routine + afzonderlijke karaktervervanging in 95 seconden
  • de regex is na 15 minuten afgebroken.

Other episodes