Modo efficiente per rimuovere TUTTI gli spazi bianchi da String?

Sto chiamando un’API REST e sto ricevendo una risposta XML. Restituisce un elenco di nomi di spazi di lavoro e sto scrivendo un rapido metodo IsExistingWorkspace() . Poiché tutte le aree di lavoro sono costituite da caratteri contigui senza spazi vuoti, sto assumendo il modo più semplice per scoprire se un particolare spazio di lavoro è presente nell’elenco è quello di rimuovere tutti gli spazi bianchi (incluse le nuove righe) e farlo (XML è la stringa ricevuta dal Web richiesta):

 XML.Contains("" + workspaceName + ""); 

So che è case-sensitive, e mi sto basando su 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 per la velocità.

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

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

Ho un modo alternativo senza regexp, e sembra funzionare piuttosto bene. È una continuazione sulla risposta di Brandon Moretz:

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

L’ho provato con 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.

Prova il metodo di sostituzione della stringa in C #.

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

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

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

Le tempistiche per il ciclo di 10.000 su una stringa semplice con spazi bianchi includono nuove linee e tabulazioni

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

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

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

Basandosi sulla risposta di Henks, ho creato alcuni metodi di prova con la sua risposta e alcuni metodi più ottimizzati. Ho trovato i risultati diversi in base alla dimensione della stringa di input. Pertanto, ho provato con due set di risultati. Nel metodo più veloce, la sorgente collegata ha un modo ancora più veloce. Ma, dal momento che è caratterizzato come non sicuro, l’ho lasciato fuori.

Risultati stringa di input lunghi:

  1. InPlaceCharArray: 2021 ms ( risposta di Sunsetquest ) – ( fonte originale )
  2. Lettore di stringhe: 6082 ms
  3. LINQ utilizzando il carattere nativo. IsWhitespace: 7357 ms
  4. LINQ: 7746 ms ( risposta di Henk )
  5. ForLoop: 32320 ms
  6. RegexCompiled: 37157 ms
  7. Regex: 42940 ms

Brevi risultati di stringa di input:

  1. InPlaceCharArray: 108 ms ( risposta di Sunsetquest ) – ( fonte originale )
  2. Lettore di stringhe: 327 ms
  3. ForLoop: 343 ms
  4. LINQ utilizzando il carattere nativo. IsWhitespace: 624 ms
  5. LINQ: 645ms ( risposta di Henk )
  6. RegexCompiled: 1671 ms
  7. 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 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); } } 

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 di 1.000.000 di loop su "This is a simple Test"

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

Se hai bisogno di prestazioni eccellenti, dovresti evitare LINQ ed espressioni regolari in questo caso. Ho fatto un po ‘di benchmarking delle prestazioni, e sembra che se vuoi eliminare lo spazio bianco dall’inizio e dalla fine della stringa, string.Trim () è la tua 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); } 

Ho trovato una bella recensione su questo su CodeProject di Felipe Machado (con l’aiuto di Richard Robertson )

Ha provato dieci diversi metodi. 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 più veloce e sicura

 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 punti di riferimento indipendenti su Stack Overflow di Stian Standahl che mostrano anche come la funzione di Felipe sia circa il 300% più veloce della prossima funzione più veloce.

Regex è eccessivo; basta usare l’estensione sulla stringa (grazie Henk). Questo è banale e avrebbe dovuto far parte del quadro. 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()); } } 

Dovevo sostituire lo spazio bianco in una stringa con spazi, ma non spazi duplicati. ad esempio, avevo bisogno di convertire qualcosa come il seguente:

 "abc\r\nd\t\t\te" 

a

 "abcde" 

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

Ecco una semplice alternativa lineare alla soluzione RegEx. Non sono sicuro di quale sia 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(); } 

Presumo che la tua risposta XML assomigli a questo:

 var xml = @"  foo   bar  "; 

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

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.

Ho trovato risultati diversi per essere vero. Sto cercando di sostituire tutti gli spazi bianchi con un singolo 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 precedente 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 sopra riportata lo fa in 25 secondi
  • la suddetta routine + sostituzione separata dei caratteri in 95 secondi
  • l’espressione regolare è stata interrotta dopo 15 minuti.

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

Possiamo usare System.Linq e possiamo farlo in una riga:

 string text = "My text with white spaces..."; text = new string(text.ToList().Where(c => c != ' ').ToArray()); 
 String s = Console.ReadLine(); s = s.Replace(" ", String.Empty); String[] arr = s.Split(' '); foreach(char num in s) { Console.WriteLine(num); } 

Questo blocco di codice rimuove tutti gli spazi dalla stringa.