¿Manera eficiente de eliminar TODOS los espacios en blanco de String?

Estoy llamando a una API REST y estoy recibiendo una respuesta XML de nuevo. Devuelve una lista de nombres de espacios de trabajo, y estoy escribiendo un IsExistingWorkspace() rápido IsExistingWorkspace() . Dado que todos los espacios de trabajo constan de caracteres contiguos sin espacios en blanco, supongo que la forma más sencilla de averiguar si un área de trabajo concreta está en la lista es eliminar todos los espacios en blanco (incluidas las líneas nuevas) y hacer esto (XML es la cadena recibida de la web solicitud):

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

Sé que es sensible a las mayúsculas y confío en eso. Solo necesito una forma de eliminar todos los espacios en blanco en una cadena de manera eficiente. Sé que RegEx y LINQ pueden hacerlo, pero estoy abierto a otras ideas. Lo que más me preocupa es la velocidad.

Esta es la forma más rápida que conozco, aunque dijiste que no querías usar expresiones regulares:

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

Tengo una forma alternativa sin expresiones regulares, y parece funcionar bastante bien. Es una continuación de la respuesta de Brandon Moretz:

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

Lo probé en una prueba de unidad simple:

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

Para 1,000,000 de bashs, la primera opción (sin regexp) se ejecuta en menos de un segundo (700 ms en mi máquina), y la segunda toma 3,5 segundos.

Pruebe el método de reemplazo de la cadena en C #.

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

Mi solución es usar Dividir y Unirme y es sorprendentemente rápido, de hecho la más rápida de las respuestas principales aquí.

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

Tiempos de 10.000 bucles en una cadena simple con espacios en blanco inc nuevas líneas y tabs

  • dividir / unir = 60 milisegundos
  • linq chararray = 94 milisegundos
  • regex = 437 milisegundos

Mejore esto envolviéndolo en un método para darle significado, y también conviértalo en un método de extensión mientras lo hagamos …

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

Sobre la base de la respuesta de Henks , he creado algunos métodos de prueba con su respuesta y algunos métodos adicionales y más optimizados. Encontré que los resultados difieren según el tamaño de la cadena de entrada. Por lo tanto, he probado con dos conjuntos de resultados. En el método más rápido, la fuente vinculada tiene una forma aún más rápida. Pero, dado que se caracteriza como inseguro, lo he omitido.

Resultados de cadena de entrada larga:

  1. InPlaceCharArray: 2021 ms ( respuesta de Sunsetquest ) – ( Fuente original )
  2. Lector de cadenas: 6082 ms
  3. LINQ que usa el carácter nativo char.IsWhitespace: 7357 ms
  4. LINQ: 7746 ms ( respuesta de Henk )
  5. ForLoop: 32320 ms
  6. RegexComstackdo: 37157 ms
  7. Regex: 42940 ms

Resultados de cadenas de entrada cortas:

  1. InPlaceCharArray: 108 ms ( respuesta de Sunsetquest ) – ( Fuente original )
  2. Lector de cadenas: 327 ms
  3. ForLoop: 343 ms
  4. LINQ que usa el carácter nativo char.IsWhitespace: 624 ms
  5. LINQ: 645ms ( respuesta de Henk )
  6. RegexComstackdo: 1671 ms
  7. Regex: 2599 ms

Código :

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

Pruebas :

 [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 una alternativa porque se ve bastante bien 🙂 – NOTA: la respuesta de Henks es la más rápida de estas.

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

Probando 1,000,000 bucles en "This is a simple Test"

Este método = 1,74 segundos
Regex = 2.58 segundos
new String (Henks) = 0.82

Si necesita un rendimiento excelente, debe evitar LINQ y expresiones regulares en este caso. Hice algunos benchmarking de rendimiento, y parece que si quieres quitar el espacio en blanco del principio y el final de la cadena, string.Trim () es tu última función.

Si necesita quitar todos los espacios en blanco de una cadena, el siguiente método funciona más rápido de lo que se ha publicado aquí:

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

Encontré un buen artículo sobre esto en CodeProject por Felipe Machado (con la ayuda de Richard Robertson )

Él probó diez métodos diferentes. Esta es la versión insegura más rápida …

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

Y la versión más segura y segura

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

También hay algunos buenos puntos de referencia independientes en Stack Overflow de Stian Standahl que también muestran cómo la función de Felipe es aproximadamente un 300% más rápida que la siguiente función más rápida.

Regex es exagerado; solo usa la extensión en la cadena (gracias Henk). Esto es trivial y debería haber sido parte del marco. De todos modos, aquí está mi implementación:

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

Necesitaba reemplazar el espacio en blanco en una cadena con espacios, pero no duplicar espacios. por ejemplo, necesitaba convertir algo como lo siguiente:

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

a

 "abcde" 

Usé el siguiente método

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

Aquí hay una alternativa lineal simple a la solución RegEx. No estoy seguro de cuál es más rápido; tendrías que compararlo.

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

Supongo que su respuesta XML se ve así:

 var xml = @"  foo   bar  "; 

La mejor manera de procesar XML es usar un analizador XML, como LINQ to XML :

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

Aquí hay otra variante:

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

Al igual que con la mayoría de las otras soluciones, no he realizado pruebas exhaustivas de referencia, pero esto funciona lo suficientemente bien para mis propósitos.

He encontrado diferentes resultados para ser verdad. Estoy tratando de reemplazar todos los espacios en blanco con un solo espacio y la expresión regular fue extremadamente lenta.

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

Lo que funcionó de la manera más óptima para mí (en C ++ cli) fue:

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

Probé la rutina anterior primero reemplazando cada carácter por separado, pero tuve que pasar a hacer subcadenas para las secciones no espaciales. Al aplicar a una cadena de 1,200,000 caracteres:

  • la rutina anterior lo hace en 25 segundos
  • la rutina anterior + reemplazo de caracteres por separado en 95 segundos
  • la expresión regular se abortó después de 15 minutos.

Nosotros podemos usar:

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

Podemos usar System.Linq y podemos hacerlo en una línea:

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

Este bloque de código elimina todos los espacios de la cadena.