Modo efficiente per rimuovere TUTTI gli spazi bianchi da String?


358

Sto chiamando un'API REST e sto ricevendo una risposta XML. Restituisce un elenco di nomi di un'area di lavoro e sto scrivendo un IsExistingWorkspace()metodo rapido . Dal momento che tutte le aree di lavoro sono costituite da caratteri contigui senza spazi bianchi, presumo che il modo più semplice per scoprire se un determinato spazio di lavoro sia nell'elenco sia rimuovere tutti gli spazi bianchi (comprese le nuove righe) e farlo (XML è la stringa ricevuta dal web richiesta):

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

So che fa distinzione tra maiuscole e minuscole e mi affido a questo. Ho solo bisogno di un modo per rimuovere tutti gli spazi bianchi in una stringa in modo efficiente. So che RegEx e LINQ possono farlo, ma sono aperto ad altre idee. Sono principalmente preoccupato solo per la velocità.


6
L'analisi di XML con regex è quasi altrettanto negativa dell'analisi di HTML con regex .
dtb,

3
@henk holterman; Vedi la mia risposta qui sotto, regexp non sembra essere il più veloce in tutti i casi.
Henk J Meulekamp,

Regex non sembra affatto il più veloce. Ho riassunto i risultati in molti modi diversi per rimuovere gli spazi bianchi da una stringa. La sintesi è in una risposta qui sotto - stackoverflow.com/a/37347881/582061
Stian Standahl

Risposte:


616

Questo è il modo più veloce che conosco, anche se hai detto che non volevi usare le espressioni regolari:

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

1
Potrei usare un'espressione regolare, non sono sicuro che sia il modo più veloce.
Corey Ogburn,

1
Sono abbastanza sicuro che lo sia. Per lo meno dietro le quinte devi controllare ogni personaggio, e questo sta solo facendo una ricerca lineare.
slandau,

19
Non dovrebbe essere Regex.Replace(XML, @"\s+", "")?
Jan-Peter Vos,

61
Se prevedi di farlo più di una volta, crea e archivia un'istanza Regex. Ciò eviterà il sovraccarico di costruirlo ogni volta, il che è più costoso di quanto si possa pensare. private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
hypehuman

10
Per coloro che non conoscono RegEx e sono alla ricerca di una spiegazione sul significato di questa espressione, \ssignifica "abbina qualsiasi token spazio bianco" e +significa "abbina uno o più token procedenti". Inoltre RegExr è un bel sito Web con cui esercitarsi a scrivere espressioni RegEx, se si desidera sperimentare.
jrh

181

Ho un modo alternativo senza regexp e sembra funzionare abbastanza bene. È una continuazione della risposta di Brandon Moretz:

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

L'ho testato in un semplice test unitario:

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

Per 1.000.000 di tentativi la prima opzione (senza regexp) viene eseguita in meno di un secondo (700 ms sulla mia macchina) e la seconda richiede 3,5 secondi.


40
.ToCharArray()non è necessario; puoi usare .Where()direttamente su una stringa.
Programma FOX

10
Solo per notare qui. Regex è più lento ... su stringhe piccole! Se dici di avere una versione digitalizzata di un volume sulla legge fiscale degli Stati Uniti (~ milioni di parole?), Con una manciata di iterazioni, Regex è di gran lunga il re! Non è ciò che è più veloce, ma ciò che dovrebbe essere usato in quale circostanza. Hai provato solo metà dell'equazione qui. -1 fino a quando non si dimostra la seconda metà del test in modo che la risposta fornisca ulteriori informazioni su quando dovrebbe essere utilizzato.
Piotr Kula

17
@ppumkin Ha chiesto una rimozione a passaggio singolo di spazi bianchi. Non più iterazioni di altre elaborazioni. Non trasformerò questa rimozione di spazi bianchi a passaggio singolo in un post esteso sull'elaborazione del testo di benchmarking.
Henk J Meulekamp,

1
Hai detto che questa volta preferiva non usare regex ma non hai detto perché.
Piotr Kula

2
@ProgramFOX, in una domanda diversa (non riesco a trovarlo facilmente) Ho notato che almeno in alcune query, l'utilizzo ToCharArrayè più veloce dell'uso .Where()diretto sulla stringa. Questo ha a che fare con l'overhead in IEnumerable<>ogni fase di iterazione e l' ToCharArrayessere molto efficiente (copia a blocchi) e il compilatore ottimizza l'iterazione su array. Perché questa differenza esiste, nessuno è stato in grado di spiegarmi, ma misura prima di rimuovere ToCharArray().
Abel,

87

Prova il metodo di sostituzione della stringa in C #.

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

28
Non rimuove le schede o le nuove righe. Se eseguo più rimozioni ora eseguo più passaggi sulla stringa.
Corey Ogburn,

11
Downvote per non aver rimosso tutti gli spazi bianchi, come fanno le risposte di slandau e Henk.
Matt Sach,

@MattSach perché non rimuove TUTTI gli spazi bianchi?
Zapnologica,

4
@Zapnologica Sostituisce solo i caratteri dello spazio. L'OP ha chiesto anche la sostituzione di nuove righe (che sono caratteri "spazi bianchi", anche se non sono caratteri spaziali).
Matt Sach,

75

La mia soluzione è usare Split and Join ed è sorprendentemente veloce, in effetti la più veloce delle risposte migliori qui.

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

Tempi per 10.000 cicli su una semplice stringa con spazi bianchi inclusi nuove righe e schede

  • split / join = 60 millisecondi
  • linq chararray = 94 millisecondi
  • regex = 437 millisecondi

Migliora questo avvolgendolo nel metodo per dargli un significato, e anche renderlo un metodo di estensione mentre ci siamo ...

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

3
Mi piace molto questa soluzione, ne sto usando una simile dai tempi pre-LINQ. Sono davvero impressionato dalle prestazioni di LINQ e un po 'sorpreso dalla regex. Forse il codice non era ottimale come avrebbe potuto essere per regex (ad esempio, dovrai memorizzare nella cache l'oggetto regex). Ma il nocciolo del problema è che la "qualità" dei dati conta molto. Forse con stringhe lunghe il regex supererà le altre opzioni. Sarà un divertente punto di riferimento da eseguire ... :-)
Loudenvier

1
In che modo default (string []) == un elenco di tutti i caratteri degli spazi bianchi? Lo vedo funzionare, ma non capisco come?
Jake Drew,

5
@kernowcode Intendi l'ambiguità tra i 2 sovraccarichi con string[]e char[]? è sufficiente specificare quale si desidera ad esempio: string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. Questo è in realtà ciò che fa la tua chiamata defaultin questo caso dato che ritorna nullanche: aiuta il compilatore a decidere quale sovraccarico scegliere. Da qui il mio commento perché l'affermazione nel tuo commento "Split ha bisogno di un array valido e null non farà ..." è falsa. Non è un grosso problema, ho pensato di ricordare solo da quando Jake Drew ha chiesto come funzionasse. +1 per la tua risposta
Frank J

6
Bella idea ... ma lo farei come segue:string.Concat("H \ne llo Wor ld".Split())
michaelkrisper,

3
la soluzione michaelkrisper è molto leggibile. Ho fatto un test e 'split / join' (162 millisecondi) ha funzionato meglio di 'split / concat' (180 millisecondi) per 10.000 iterazioni della stessa stringa.
codice kernow

45

Sulla base della risposta di Henks ho creato alcuni metodi di prova con la sua risposta e alcuni metodi aggiunti, più ottimizzati. Ho trovato che i risultati differiscono in base alla dimensione della stringa di input. Pertanto, ho testato con due set di risultati. Nel metodo più veloce, l'origine collegata ha un modo ancora più veloce. Ma, poiché è caratterizzato come non sicuro, l'ho lasciato fuori.

Risultati della stringa di input lunghi:

  1. InPlaceCharArray: 2021 ms ( risposta di Sunsetquest ) - ( fonte originale )
  2. Divisione stringa quindi join: 4277ms ( risposta di Kernowcode )
  3. Lettore di stringhe: 6082 ms
  4. LINQ utilizzando char.IsWhitespace nativo: 7357 ms
  5. LINQ: 7746 ms ( risposta di Henk )
  6. ForLoop: 32320 ms
  7. RegexCompiled: 37157 ms
  8. Regex: 42940 ms

Risultati di stringhe di input brevi:

  1. InPlaceCharArray: 108 ms ( risposta di Sunsetquest ) - ( fonte originale )
  2. Divisione stringa quindi join: 294 ms ( risposta di Kernowcode )
  3. Lettore di stringhe: 327 ms
  4. ForLoop: 343 ms
  5. LINQ utilizzando char.IsWhitespace nativo: 624 ms
  6. LINQ: 645ms ( risposta di Henk )
  7. RegexCompiled: 1671 ms
  8. Regex: 2599 ms

Codice :

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

Test :

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

Modifica : testato un bel rivestimento da Kernowcode.


24

Solo un'alternativa perché sembra abbastanza carina :) - NOTA: la risposta di Henks è la più veloce di queste.

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

Test su 1.000.000 di loop attivi "This is a simple Test"

Questo metodo = 1,74 secondi
Regex = 2,58 secondi
new String(Henks) = 0,82


1
Perché questo è stato downvoted? È perfettamente accettabile, soddisfa i requisiti, funziona più velocemente dell'opzione RegEx ed è molto leggibile?
BlueChippy

4
perché può essere scritto molto più breve: nuova stringa (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit,

7
Potrebbe essere vero, ma la risposta è ancora valida, è leggibile, più veloce di regex e produce il risultato desiderato. Molte delle altre risposte sono DOPO questo ... quindi un downvote non ha senso.
BlueChippy

2
C'è un'unità per "0,82"? O è una misura relativa (82%)? Puoi modificare la tua risposta per renderla più chiara?
Peter Mortensen,

20

Ho trovato un bel commento su questo su CodeProject di Felipe Machado (con l'aiuto di Richard Robertson )

Ha testato dieci metodi diversi. Questa è la versione più veloce non sicura ...

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

E la versione sicura più veloce ...

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

Ci sono anche alcuni bei benchmark indipendenti su Stack Overflow di Stian Standahl che mostrano anche come la funzione di Felipe sia circa il 300% più veloce della successiva funzione più veloce.


Ho provato a tradurre questo in C ++ ma sono un po 'bloccato. Qualche idea sul perché la mia porta potrebbe non funzionare? stackoverflow.com/questions/42135922/…
Jon Cage

2
Non posso resistere. Guarda nella sezione commenti dell'articolo a cui ti riferisci. Mi troverai come "Basketcase Software". Ci ha lavorato insieme per un po '. Me ne ero completamente dimenticato quando questo problema si ripresentò. Grazie per i bei ricordi. :)
Richard Robertson,

1
E se si desidera rimuovere solo WS extra? Che dire di questo stackoverflow.com/questions/17770202/… mod?
Tom

Il più veloce è un po 'più lento ;-) La stringa come contenitore si perfeziona qui (nell'app 4:15 a 3:55 => 8,5% in meno, ma quando lascia la stringa 3:30 => 21,4% in meno e il profiller mostra circa il 50% speso in questo metodo). Quindi, nella vera stringa live dovrebbe essere circa il 40% più veloce rispetto alla conversione (lenta) dell'array usata qui.
Tom,

15

Se hai bisogno di prestazioni eccezionali, in questo caso dovresti evitare LINQ e le espressioni regolari. Ho eseguito alcuni benchmark delle prestazioni e sembra che se si desidera eliminare gli spazi bianchi dall'inizio e dalla fine della stringa, string.Trim () è la funzione definitiva.

Se devi rimuovere tutti gli spazi bianchi da una stringa, il seguente metodo funziona più velocemente di tutto ciò che è stato pubblicato qui:

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

Sarei curioso di conoscere i dettagli dei tuoi benchmarking - non che io sia scettico, ma sono curioso del sovraccarico coinvolto in Linq. Quanto è stato male?
Mark Meuer,

Non ho rieseguito tutti i test, ma posso ricordare molto: tutto ciò che riguardava Linq era molto più lento di qualsiasi altro senza di esso. Tutto l'uso intelligente delle funzioni stringa / char e dei costruttori non ha fatto alcuna differenza percentuale se si utilizzava Linq.
JHM,

11

Regex è eccessivo; usa l'estensione sulla stringa (grazie Henk). Questo è banale e avrebbe dovuto far parte del framework. Comunque, ecco la mia implementazione:

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

questa è sostanzialmente una risposta non necessaria (regex è eccessivo, ma è una soluzione più rapida di quella data - ed è già accettata?)
W1ll1amvl

Come si possono usare i metodi di estensione Linq su una stringa? Non riesco a capire quale utilizzo mi manchi diverso daSystem.Linq
GGirard,

Ok sembra che questo non sia disponibile in PCL, IEnumerable <char> è condizionale nell'implementazione di Microsoft String ... E sto usando Profile259 che non supporta questo :)
GGirard,

4

Ecco una semplice alternativa lineare alla soluzione RegEx. Non sono sicuro di quale sia il più veloce; dovresti confrontarlo.

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

Avevo bisogno di sostituire gli spazi bianchi in una stringa con spazi, ma non spazi duplicati. ad esempio, avevo bisogno di convertire qualcosa di simile al seguente:

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

per

"a b c d e"

Ho usato il seguente metodo

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

2

Presumo che la tua risposta XML assomigli a questo:

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

Il modo migliore per elaborare XML è utilizzare un parser XML, come LINQ to XML :

var doc = XDocument.Parse(xml);

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

Una volta verificato che un determinato tag <name> ha il valore corretto, ho finito. L'analisi del documento non avrebbe un certo sovraccarico?
Corey Ogburn,

4
Certo, ha un po 'di spese generali. Ma ha il vantaggio di essere corretto. Una soluzione basata ad esempio su regex è molto più difficile da ottenere. Se si determina che una soluzione LINQ to XML è troppo lenta, è sempre possibile sostituirla con qualcosa di più veloce. Ma dovresti evitare di cercare l'implementazione più efficiente prima di sapere che quella corretta è troppo lenta.
dtb,

Questo sarà in esecuzione nei server back-end del mio datore di lavoro. Il peso leggero è quello che sto cercando. Non voglio qualcosa che "funzioni" ma sia ottimale.
Corey Ogburn,

4
LINQ to XML è uno dei modi più leggeri per lavorare correttamente con XML in .NET
dtb

1

Ecco un'altra variante:

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

Come con la maggior parte delle altre soluzioni, non ho eseguito test di benchmark esaustivi, ma questo funziona abbastanza bene per i miei scopi.


1

Possiamo usare:

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

Questo è quasi esattamente lo stesso della risposta di Henk sopra. L'unica differenza è che controlli null.
Corey Ogburn,

Sì, verifica la presenza di null è importante
Tarik BENARAB

1
Forse questo avrebbe dovuto essere solo un commento sulla sua risposta. Sono contento che tu l'abbia sollevato però. Non sapevo che i metodi di estensione potessero essere chiamati su oggetti null.
Corey Ogburn,

0

Ho trovato diversi risultati per essere vero. Sto cercando di sostituire tutti gli spazi bianchi con un unico spazio e la regex è stata estremamente lenta.

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

Ciò che ha funzionato in modo ottimale per me (in C ++ cli) è stato:

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

Ho provato prima la routine sopra sostituendo ogni carattere separatamente, ma ho dovuto passare a fare sottostringhe per le sezioni non spaziali. Quando si applica a una stringa di 1.200.000 caratteri:

  • la routine di cui sopra viene eseguita in 25 secondi
  • la routine sopra + sostituzione del carattere separato in 95 secondi
  • la regex si interruppe dopo 15 minuti.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.