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

359

Llamo a una API REST y recibo una respuesta XML. Devuelve una lista de nombres de espacios de trabajo, y estoy escribiendo un IsExistingWorkspace()método rápido . Dado que todos los espacios de trabajo consisten en caracteres contiguos sin espacios en blanco, supongo que la forma más fácil de averiguar si un espacio de trabajo en particular está en la lista es eliminar todos los espacios en blanco (incluidas las nuevas líneas) y hacer esto (XML es la cadena recibida de la web solicitud):

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

Sé que distingue entre mayúsculas y minúsculas, y estoy confiando en eso. Solo necesito una forma de eliminar todos los espacios en blanco de una cadena de manera eficiente. Sé que RegEx y LINQ pueden hacerlo, pero estoy abierto a otras ideas. Principalmente solo me preocupa la velocidad.

Corey Ogburn
fuente
66
Analizar XML con expresiones regulares es casi tan malo como analizar HTML con expresiones regulares .
dtb
3
@henk holterman; Vea mi respuesta a continuación, regexp no parece ser el más rápido en todos los casos.
Henk J Meulekamp
Regex no parece ser el más rápido en absoluto. He resumido los resultados de muchas maneras diferentes para eliminar espacios en blanco de una cadena. El resumen se encuentra en una respuesta a continuación: stackoverflow.com/a/37347881/582061
Stian Standahl

Respuestas:

617

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

Regex.Replace(XML, @"\s+", "")
slandau
fuente
1
Podría usar una expresión regular, pero no estoy seguro de si es la forma más rápida.
Corey Ogburn
1
Estoy bastante seguro de que es así. Por lo menos, detrás de escena, debe verificar cada personaje, y esto es solo una búsqueda lineal.
slandau
19
¿No debería ser eso Regex.Replace(XML, @"\s+", "")?
Jan-Peter Vos
61
Si planea hacer esto más de una vez, cree y almacene una instancia de Regex. Esto ahorrará la sobrecarga de construirlo cada vez, lo cual es más costoso de lo que piensas. private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
hipehumano
10
Para aquellos que son nuevos en RegEx y buscan una explicación de lo que significa esta expresión, \ssignifica "hacer coincidir cualquier token de espacio en blanco" y +significa "hacer coincidir uno o más de los tokens en curso". También RegExr es un buen sitio web para practicar escribir expresiones RegEx, si quieres experimentar.
jrh
181

Tengo una forma alternativa sin regexp, 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 unitaria 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 intentos, 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.

Henk J Meulekamp
fuente
40
.ToCharArray()no es necesario; puedes usar .Where()directamente en una cadena.
ProgramFOX
10
Solo para notar aquí. Regex es más lento ... en pequeñas cadenas! Si dice que tenía una versión digitalizada de un Volumen sobre la Ley Tributaria de los EE. UU. (¿~ Millones de palabras?), Con un puñado de iteraciones, ¡Regex es el rey, con mucho! No es lo que es más rápido, sino lo que debe usarse en qué circunstancia. Aquí solo probaste la mitad de la ecuación. -1 hasta que pruebe la segunda mitad de la prueba para que la respuesta proporcione más información sobre cuándo se debe usar.
Piotr Kula
17
@ppumkin Solicitó la eliminación de un solo espacio en blanco. No múltiples iteraciones de otro procesamiento. No voy a hacer esta eliminación de espacios en blanco de una sola pasada en una publicación extendida sobre el procesamiento de texto de evaluación comparativa.
Henk J Meulekamp
1
Dijiste que esta vez prefería no usar expresiones regulares, pero no dijiste por qué.
Piotr Kula
2
@ProgramFOX, en una pregunta diferente (no puedo encontrarlo fácilmente), noté que al menos en algunas consultas, usar ToCharArrayes más rápido que usar .Where()directamente en la cadena. Esto tiene algo que ver con la sobrecarga IEnumerable<>en cada paso de iteración, y al ToCharArrayser muy eficiente (copia en bloque) y el compilador optimiza la iteración sobre las matrices. Por qué existe esta diferencia, nadie ha podido explicarme, pero mide antes de eliminar ToCharArray().
Abel
87

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

XML.Replace(" ", string.Empty);
Mike_K
fuente
28
No elimina pestañas ni líneas nuevas. Si hago múltiples eliminaciones ahora estoy haciendo múltiples pases sobre la cadena.
Corey Ogburn
11
Voto negativo por no eliminar todos los espacios en blanco, como lo hacen las respuestas de slandau y Henk.
Matt Sach
@MattSach ¿por qué no elimina TODOS los espacios en blanco?
Zapnologica
44
@Zapnologica Solo reemplaza los caracteres espaciales. El OP también solicitó el reemplazo de las nuevas líneas (que son caracteres de "espacio en blanco", a pesar de que no son caracteres de espacio).
Matt Sach
75

Mi solución es usar Split and Join y es sorprendentemente rápido, de hecho, la respuesta más rápida aquí.

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

Tiempos para 10,000 bucles en una cadena simple con espacios en blanco con nuevas líneas y pestañas

  • dividir / unir = 60 milisegundos
  • linq chararray = 94 milisegundos
  • expresión regular = 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 estamos en ello ...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
kernowcode
fuente
3
Realmente me gusta esta solución, he estado usando una similar desde los días anteriores a LINQ. De hecho, estoy impresionado con el rendimiento de LINQ y algo sorprendido con la expresión regular. Tal vez el código no fue tan óptimo como podría haber sido para la expresión regular (por ejemplo, tendrá que almacenar en caché el objeto de expresión regular). Pero el quid del problema es que la "calidad" de los datos será muy importante. Quizás con cadenas largas la expresión regular superará a las otras opciones. Será un punto de referencia divertido para realizar ... :-)
Loudenvier
1
¿Cómo hace default (string []) == una lista de todos los espacios en blanco? Lo veo funcionando, pero no entiendo cómo.
Jake Drew
55
@kernowcode ¿Te refieres a la ambigüedad entre las 2 sobrecargas con string[]y char[]? sólo hay que especificar cuál de ellos desea por ejemplo: string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. En realidad, eso es lo que hace su llamada defaulten este caso, ya que también regresa null: ayuda al compilador a decidir qué sobrecarga elegir. De ahí mi comentario porque la afirmación en su comentario "Split necesita una matriz válida y nulo no funcionará ..." es falsa. No es gran cosa, solo pensé que valía la pena mencionarlo ya que Jake Drew preguntó cómo funcionaba. +1 por su respuesta
Frank J
66
Buena idea ... pero lo haría de la siguiente manera:string.Concat("H \ne llo Wor ld".Split())
michaelkrisper
3
La solución de michaelkrisper es muy legible. Hice una prueba y 'split / join' (162 milisegundos) funcionó mejor que 'split / concat' (180 milisegundos) para 10,000 iteraciones de la misma cadena.
kernowcode
45

Sobre la base de la respuesta de Henks , he creado algunos métodos de prueba con su respuesta y algunos métodos 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 por ser inseguro, he dejado esto fuera.

Resultados de cadena de entrada largos:

  1. InPlaceCharArray: 2021 ms ( respuesta de Sunsetquest ) - ( Fuente original )
  2. División de cadena y luego unión: 4277 ms ( respuesta de Kernowcode )
  3. Lector de cadenas: 6082 ms
  4. LINQ utilizando char.IsWhitespace nativo: 7357 ms
  5. LINQ: 7746 ms ( respuesta de Henk )
  6. ForLoop: 32320 ms
  7. Regex Compilado: 37157 ms
  8. Regex: 42940 ms

Resultados cortos de la cadena de entrada:

  1. InPlaceCharArray: 108 ms ( respuesta de Sunsetquest ) - ( Fuente original )
  2. División de cadena y luego unión: 294 ms ( respuesta de Kernowcode )
  3. Lector de cadenas: 327 ms
  4. ForLoop: 343 ms
  5. LINQ usando char.IsWhitespace nativo: 624 ms
  6. LINQ: 645ms ( la respuesta de Henk )
  7. Regex Compilado: 1671 ms
  8. 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 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);
    }
}

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

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

Editar : Probé un buen revestimiento de Kernowcode.

Stian Standahl
fuente
24

Solo una alternativa porque se ve bastante bien :) - NOTA: La respuesta de Henks es la más rápida de todas.

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

Probar 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

BlueChippy
fuente
1
¿Por qué se rechazó esto? ¿Es perfectamente aceptable, cumple con los requisitos, funciona más rápido que la opción RegEx y es muy legible?
BlueChippy
44
porque se puede escribir mucho más corto: nueva cadena (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit
77
Puede ser cierto, pero la respuesta sigue en pie, es legible, más rápida que la expresión regular y produce el resultado deseado. Muchas de las otras respuestas son DESPUÉS de esta ... por lo tanto, un voto negativo no tiene sentido.
BlueChippy
2
¿Hay una unidad para "0.82"? ¿O es una medida relativa (82%)? ¿Puedes editar tu respuesta para hacerlo más claro?
Peter Mortensen
20

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

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 segura más rápida ...

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.

Sunsetquest
fuente
Intenté traducir esto a C ++ pero estoy un poco atascado. ¿Alguna idea de por qué mi puerto podría estar fallando? stackoverflow.com/questions/42135922/…
Jon Cage
2
No me puedo resistir Mire en la sección de comentarios del artículo al que se refiere. Me encontrarás como "Software Basketcase". Él y trabajaron juntos en esto por un tiempo. Me había olvidado por completo de esto cuando este problema volvió a surgir. Gracias por los buenos recuerdos. :)
Richard Robertson
1
¿Y qué pasa si solo quieres eliminar WS extra? ¿Qué pasa con este stackoverflow.com/questions/17770202/… mod?
Tom
El más rápido es un poco más lento ;-) La cadena como contenedor funciona mejor aquí (en la aplicación 4:15 a 3:55 => 8.5% menos, pero cuando se deja la cadena 3:30 => 21.4% menos y el perfil muestra alrededor del 50% gastado en este método). Entonces, en una cadena real en vivo, debería ser alrededor de un 40% más rápido en comparación con la conversión de matriz (lenta) utilizada aquí.
Tom
15

Si necesita un rendimiento excelente, debe evitar LINQ y expresiones regulares en este caso. Hice algunas evaluaciones comparativas de rendimiento, y parece que si desea eliminar el espacio en blanco desde el principio y el final de la cadena, string.Trim () es su función final.

Si necesita quitar todos los espacios en blanco de una cadena, el siguiente método funciona más rápido de todo 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);
    }
JHM
fuente
Me gustaría saber los detalles de sus evaluaciones comparativas, no es que sea escéptico, pero tengo curiosidad sobre los gastos generales relacionados con Linq. Que tan malo fue
Mark Meuer
No he vuelto a ejecutar todas las pruebas, pero puedo recordar esto: todo lo que involucraba a Linq era mucho más lento que cualquier cosa sin él. Todo el uso inteligente de las funciones y constructores de cadenas / caracteres no hizo una diferencia porcentual si se usaba Linq.
JHM
11

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());
    }
}
Maksood
fuente
esta es básicamente una respuesta innecesaria (la expresión regular es exagerada, pero es una solución más rápida que la dada, ¿y ya está aceptada?)
W1ll1amvl
¿Cómo puedes usar los métodos de extensión de Linq en una cadena? No puedo entender qué uso me estoy perdiendo otros queSystem.Linq
GGirard
Ok parece que esto no está disponible en PCL, IEnumerable <char> es condicional en la implementación de Microsoft String ... Y estoy usando Profile259 que no admite esto :)
GGirard
4

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();
}
Brandon Moretz
fuente
3

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

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

a

"a b c d e"

Utilicé 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();
}
usuario1325543
fuente
2

Supongo que su respuesta XML se ve así:

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

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");
dtb
fuente
Una vez que verifico que una etiqueta particular de <nombre> tiene el valor adecuado, he terminado. ¿No analizar el documento tiene algo de sobrecarga?
Corey Ogburn
44
Claro, tiene algo de gastos generales. Pero tiene el beneficio de ser correcto. Una solución basada, por ejemplo, en expresiones regulares es mucho más difícil de acertar. Si determina que una solución LINQ to XML es demasiado lenta, siempre puede reemplazarla por algo más rápido. Pero debe evitar buscar la implementación más eficiente antes de saber que la correcta es demasiado lenta.
dtb
Esto se ejecutará en los servidores de backend de mi empleador. Ligero es lo que estoy buscando. No quiero algo que "simplemente funcione" pero que sea óptimo.
Corey Ogburn
44
LINQ to XML es una de las formas más livianas de trabajar correctamente con XML en .NET
dtb
1

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.

Fred
fuente
1

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());
    }
Tarik BENARAB
fuente
Esto es casi exactamente lo mismo que la respuesta de Henk anterior. La única diferencia es que verificas null.
Corey Ogburn
Sí, verifique si nulo es importante
Tarik BENARAB
1
Tal vez esto debería haber sido solo un comentario sobre su respuesta. Sin embargo, me alegra que lo hayas mencionado. No sabía que los métodos de extensión se podían invocar en objetos nulos.
Corey Ogburn
0

He encontrado diferentes resultados para ser verdad. Estoy tratando de reemplazar todo el espacio 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 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 );
}

Intenté la rutina anterior primero reemplazando cada carácter por separado, pero tuve que cambiar a hacer subcadenas para las secciones que no son espaciales. Cuando se aplica a una cadena de 1,200,000 caracteres:

  • la rutina anterior se realiza en 25 segundos
  • la rutina anterior + reemplazo de personaje separado en 95 segundos
  • la expresión regular abortó después de 15 minutos.
hvanbrug
fuente