Cómo reemplazar varios espacios en blanco con un espacio en blanco

108

Digamos que tengo una cadena como:

"Hello     how are   you           doing?"

Me gustaría una función que convierta varios espacios en un solo espacio.

Entonces obtendría:

"Hello how are you doing?"

Sé que podría usar expresiones regulares o llamar

string s = "Hello     how are   you           doing?".replace("  "," ");

Pero tendría que llamarlo varias veces para asegurarme de que todos los espacios en blanco secuenciales se reemplacen con solo uno.

¿Existe ya un método incorporado para esto?

Mate
fuente
¿Podría aclarar: sólo se trata de espacios, o "todos" los espacios en blanco?
Jon Skeet
¿Y desea que cualquier espacio en blanco que no sea un espacio se convierta en espacios?
Jon Skeet
Solo quise decir que todos los espacios en blanco en la serie deberían ser como máximo 1
Matt
1
Posible duplicado de stackoverflow.com/questions/206717/…
Michael Freidgeim
2 cosas a considerar: 1. char.IsWhiteSpace incluye retorno de carro, salto de línea, etc. 2. 'espacio en blanco' probablemente se prueba con más precisión con Char.GetUnicodeCategory (ch) = Globalization.UnicodeCategory.SpaceSeparator
smirkingman

Respuestas:

196
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
Tim Hoolihan
fuente
40
En mi opinión, evitar las expresiones regulares si se siente cómodo con ellas es la optimización prematura
Tim Hoolihan
8
Si su aplicación no es crítica en cuanto al tiempo, puede permitirse 1 microsegundo de procesamiento.
Daniel
16
Tenga en cuenta que '\ s' no solo reemplaza los espacios en blanco, sino también los caracteres de nueva línea.
Bart Kiers
12
buena captura, si solo quieres espacios, cambia el patrón a "[] +"
Tim Hoolihan
9
¿No debería usar '{2,}' en lugar de '+' para evitar reemplazar espacios en blanco únicos?
angularsen
52

Esta pregunta no es tan simple como lo han hecho otros carteles (y como originalmente creí que era), porque la pregunta no es tan precisa como debería ser.

Hay una diferencia entre "espacio" y "espacio en blanco". Si solo te refieres a espacios, entonces debes usar una expresión regular de " {2,}". Si te refieres a cualquier espacio en blanco, es un asunto diferente. ¿Deben convertirse todos los espacios en blanco en espacios? ¿Qué debería pasar con el espacio al principio y al final?

Para el punto de referencia a continuación, asumí que solo te preocupan los espacios y que no quieres hacer nada con los espacios individuales, incluso al principio y al final.

Tenga en cuenta que la corrección es casi siempre más importante que el rendimiento. El hecho de que la solución Split / Join elimine cualquier espacio en blanco inicial / final (incluso solo espacios simples) es incorrecto en lo que respecta a sus requisitos especificados (que pueden estar incompletos, por supuesto).

El punto de referencia utiliza MiniBench .

using System;
using System.Text.RegularExpressions;
using MiniBench;

internal class Program
{
    public static void Main(string[] args)
    {

        int size = int.Parse(args[0]);
        int gapBetweenExtraSpaces = int.Parse(args[1]);

        char[] chars = new char[size];
        for (int i=0; i < size/2; i += 2)
        {
            // Make sure there actually *is* something to do
            chars[i*2] = (i % gapBetweenExtraSpaces == 1) ? ' ' : 'x';
            chars[i*2 + 1] = ' ';
        }
        // Just to make sure we don't have a \0 at the end
        // for odd sizes
        chars[chars.Length-1] = 'y';

        string bigString = new string(chars);
        // Assume that one form works :)
        string normalized = NormalizeWithSplitAndJoin(bigString);


        var suite = new TestSuite<string, string>("Normalize")
            .Plus(NormalizeWithSplitAndJoin)
            .Plus(NormalizeWithRegex)
            .RunTests(bigString, normalized);

        suite.Display(ResultColumns.All, suite.FindBest());
    }

    private static readonly Regex MultipleSpaces = 
        new Regex(@" {2,}", RegexOptions.Compiled);

    static string NormalizeWithRegex(string input)
    {
        return MultipleSpaces.Replace(input, " ");
    }

    // Guessing as the post doesn't specify what to use
    private static readonly char[] Whitespace =
        new char[] { ' ' };

    static string NormalizeWithSplitAndJoin(string input)
    {
        string[] split = input.Split
            (Whitespace, StringSplitOptions.RemoveEmptyEntries);
        return string.Join(" ", split);
    }
}

Algunas ejecuciones de prueba:

c:\Users\Jon\Test>test 1000 50
============ Normalize ============
NormalizeWithSplitAndJoin  1159091 0:30.258 22.93
NormalizeWithRegex        26378882 0:30.025  1.00

c:\Users\Jon\Test>test 1000 5
============ Normalize ============
NormalizeWithSplitAndJoin  947540 0:30.013 1.07
NormalizeWithRegex        1003862 0:29.610 1.00


c:\Users\Jon\Test>test 1000 1001
============ Normalize ============
NormalizeWithSplitAndJoin  1156299 0:29.898 21.99
NormalizeWithRegex        23243802 0:27.335  1.00

Aquí, el primer número es el número de iteraciones, el segundo es el tiempo necesario y el tercero es una puntuación escalada, siendo 1.0 el mejor.

Eso muestra que en al menos algunos casos (incluido este) una expresión regular puede superar a la solución Split / Join, a veces por un margen muy significativo.

Sin embargo, si cambia a un requisito de "todos los espacios", luego de Split / Join no aparecerá en ganar. Como suele ser el caso, el diablo está en los detalles ...

Jon Skeet
fuente
1
Gran analisis. Así que parece que ambos teníamos razón en distintos grados. El código de mi respuesta se tomó de una función más grande que tiene la capacidad de normalizar todos los espacios en blanco y / o caracteres de control desde dentro de una cadena y desde el principio y el final.
Scott Dorman
1
Con solo los caracteres de espacio en blanco que especificó, en la mayoría de mis pruebas, la expresión regular y Split / Join eran casi iguales: S / J tenía un pequeño, pequeño beneficio, a costa de la corrección y la complejidad. Por esas razones, normalmente prefiero la expresión regular. No me malinterpretes, estoy lejos de ser un fanático de las expresiones regulares, pero no me gusta escribir código más complejo por el bien del rendimiento sin probar el rendimiento primero.
Jon Skeet
NormalizeWithSplitAndJoin creará mucha más basura, es difícil saber si un problema real se verá afectado más tiempo de GC que el banchmark.
Ian Ringrose
@IanRingrose ¿Qué tipo de basura se puede crear?
Dronz
18

Un expressoin regular sería la forma más fácil. Si escribe la expresión regular de la manera correcta, no necesitará varias llamadas.

Cámbielo a esto:

string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " "); 
Brandon
fuente
Mi único problema @"\s{2,}"es que no reemplaza las pestañas individuales y otros caracteres de espacio Unicode con un espacio. Si va a reemplazar 2 pestañas con un espacio, probablemente debería reemplazar 1 pestaña con un espacio. @"\s+"lo hará por ti.
David Specht
17

Si bien las respuestas existentes están bien, me gustaría señalar un enfoque que no funciona:

public static string DontUseThisToCollapseSpaces(string text)
{
    while (text.IndexOf("  ") != -1)
    {
        text = text.Replace("  ", " ");
    }
    return text;
}

Esto puede repetirse para siempre. ¿A alguien le importa adivinar por qué? (Solo me encontré con esto cuando se hizo como una pregunta de un grupo de noticias hace unos años ... alguien realmente lo encontró como un problema).

Jon Skeet
fuente
Creo que recuerdo que esta pregunta se hizo hace un tiempo en SO. IndexOf ignora ciertos caracteres que Replace no hace. Así que el doble espacio siempre estuvo ahí, pero nunca se quitó.
Brandon
19
Se debe a que IndexOf ignora algunos caracteres Unicode, siendo el culp específico en este caso algún carácter asiático iirc. Mmmm, no ensamblador de ancho cero según Google.
ahawker
Lo aprendí de la manera difícil :( stackoverflow.com/questions/9260693/…
Antonio Bakula
Aprendí de la manera difícil. Especialmente con dos no ensambladores de ancho cero (\ u200C \ u200C). IndexOf devuelve el índice de este "espacio doble", pero Reemplazar no lo reemplaza. Creo que es porque para IndexOf, necesitas especificar StringComparsion (Ordinal) para que se comporte de la misma manera que Reemplazar. De esta forma, ninguno de estos dos localizará "espacios dobles". Más sobre StringComparsion docs.microsoft.com/en-us/dotnet/api/…
Martin Brabec
4

Como ya se señaló, esto se hace fácilmente mediante una expresión regular. Solo agregaré que es posible que desee agregar un .trim () a eso para deshacerse de los espacios en blanco iniciales / finales.

MAK
fuente
4

Aquí está la solución con la que trabajo. Sin RegEx y String.Split.

public static string TrimWhiteSpace(this string Value)
{
    StringBuilder sbOut = new StringBuilder();
    if (!string.IsNullOrEmpty(Value))
    {
        bool IsWhiteSpace = false;
        for (int i = 0; i < Value.Length; i++)
        {
            if (char.IsWhiteSpace(Value[i])) //Comparion with WhiteSpace
            {
                if (!IsWhiteSpace) //Comparison with previous Char
                {
                    sbOut.Append(Value[i]);
                    IsWhiteSpace = true;
                }
            }
            else
            {
                IsWhiteSpace = false;
                sbOut.Append(Value[i]);
            }
        }
    }
    return sbOut.ToString();
}

así que puedes:

string cleanedString = dirtyString.TrimWhiteSpace();
fubo
fuente
4

Un eliminador de espacios en blanco adicional rápido ... Este es el más rápido y se basa en la copia in situ de Felipe Machado.

static string InPlaceCharArray(string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false;
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        if (src[i] == '\u0020')
        {
            if (lastWasWS == false)
            {
                src[dstIdx++] = ch;
                lastWasWS = true;
            }
        }
        else
        { 
            lastWasWS = false;
            src[dstIdx++] = ch;
        }
    }
    return new string(src, 0, dstIdx);
}

Los puntos de referencia ...

InPlaceCharArraySpaceOnly por Felipe Machado en CodeProject 2015 y modificado por Sunsetquest para eliminación de múltiples espacios. Tiempo: 3,75 garrapatas

InPlaceCharArray de Felipe Machado 2015 y ligeramente modificado por Sunsetquest para la eliminación de varios espacios. Time 6.50 Ticks (también admite pestañas)

SplitAndJoinOnSpace por Jon Skeet . Tiempo: 13.25 garrapatas

StringBuilder de fubo Tiempo: 13,5 ticks (también admite pestañas)

Regex con compilación de Jon Skeet . Tiempo: 17 garrapatas

StringBuilder por David S 2013 Tiempo: 30,5 ticks

Regex sin compilar por Brandon Tiempo: 63.25 ticks

StringBuilder por user214147 Tiempo: 77.125 Ticks

Regex con Tim Hoolihan no compilado Tiempo: 147.25 ticks

El código de referencia ...

using System;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using System.Text;

static class Program
{
    public static void Main(string[] args)
    {
    long seed = ConfigProgramForBenchmarking();

    Stopwatch sw = new Stopwatch();

    string warmup = "This is   a Warm  up function for best   benchmark results." + seed;
    string input1 = "Hello World,    how are   you           doing?" + seed;
    string input2 = "It\twas\t \tso    nice  to\t\t see you \tin 1950.  \t" + seed;
    string correctOutput1 = "Hello World, how are you doing?" + seed;
    string correctOutput2 = "It\twas\tso nice to\tsee you in 1950. " + seed;
    string output1,output2;

    //warm-up timer function
    sw.Restart();
    sw.Stop();

    sw.Restart();
    sw.Stop();
    long baseVal = sw.ElapsedTicks;

    // InPlace Replace by Felipe Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
    output1 = InPlaceCharArraySpaceOnly (warmup);
    sw.Restart();
    output1 = InPlaceCharArraySpaceOnly (input1);
    output2 = InPlaceCharArraySpaceOnly (input2);
    sw.Stop();
    Console.WriteLine("InPlaceCharArraySpaceOnly : " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    // InPlace Replace by Felipe R. Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
    output1 = InPlaceCharArray(warmup);
    sw.Restart();
    output1 = InPlaceCharArray(input1);
    output2 = InPlaceCharArray(input2);
    sw.Stop();
    Console.WriteLine("InPlaceCharArray: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex with non-compile Tim Hoolihan (https://stackoverflow.com/a/1279874/2352507)
    string cleanedString = 
    output1 = Regex.Replace(warmup, @"\s+", " ");
    sw.Restart();
    output1 = Regex.Replace(input1, @"\s+", " ");
    output2 = Regex.Replace(input2, @"\s+", " ");
    sw.Stop();
    Console.WriteLine("Regex by Tim Hoolihan: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex with compile by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
    output1 = MultipleSpaces.Replace(warmup, " ");
    sw.Restart();
    output1 = MultipleSpaces.Replace(input1, " ");
    output2 = MultipleSpaces.Replace(input2, " ");
    sw.Stop();
    Console.WriteLine("Regex with compile by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
    output1 = SplitAndJoinOnSpace(warmup);
    sw.Restart();
    output1 = SplitAndJoinOnSpace(input1);
    output2 = SplitAndJoinOnSpace(input2);
    sw.Stop();
    Console.WriteLine("Split And Join by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //Regex by Brandon (https://stackoverflow.com/a/1279878/2352507
    output1 = Regex.Replace(warmup, @"\s{2,}", " ");
    sw.Restart();
    output1 = Regex.Replace(input1, @"\s{2,}", " ");
    output2 = Regex.Replace(input2, @"\s{2,}", " ");
    sw.Stop();
    Console.WriteLine("Regex by Brandon: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
    output1 = user214147(warmup);
    sw.Restart();
    output1 = user214147(input1);
    output2 = user214147(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder by user214147: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));

    //StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
    output1 = fubo(warmup);
    sw.Restart();
    output1 = fubo(input1);
    output2 = fubo(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder by fubo: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));


    //StringBuilder by David S 2013 (https://stackoverflow.com/a/16035044/2352507)
    output1 = SingleSpacedTrim(warmup);
    sw.Restart();
    output1 = SingleSpacedTrim(input1);
    output2 = SingleSpacedTrim(input2);
    sw.Stop();
    Console.WriteLine("StringBuilder(SingleSpacedTrim) by David S: " + (sw.ElapsedTicks - baseVal));
    Console.WriteLine("  Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
    Console.WriteLine("  Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
}

// InPlace Replace by Felipe Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArray(string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false;
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        if (src[i] == '\u0020')
        {
            if (lastWasWS == false)
            {
                src[dstIdx++] = ch;
                lastWasWS = true;
            }
        }
        else
        { 
            lastWasWS = false;
            src[dstIdx++] = ch;
        }
    }
    return new string(src, 0, dstIdx);
}

// InPlace Replace by Felipe R. Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArraySpaceOnly (string str)
{
    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;
    bool lastWasWS = false; //Added line
    for (int i = 0; i < len; i++)
    {
        var ch = src[i];
        switch (ch)
        {
            case '\u0020': //SPACE
            case '\u00A0': //NO-BREAK SPACE
            case '\u1680': //OGHAM SPACE MARK
            case '\u2000': // EN QUAD
            case '\u2001': //EM QUAD
            case '\u2002': //EN SPACE
            case '\u2003': //EM SPACE
            case '\u2004': //THREE-PER-EM SPACE
            case '\u2005': //FOUR-PER-EM SPACE
            case '\u2006': //SIX-PER-EM SPACE
            case '\u2007': //FIGURE SPACE
            case '\u2008': //PUNCTUATION SPACE
            case '\u2009': //THIN SPACE
            case '\u200A': //HAIR SPACE
            case '\u202F': //NARROW NO-BREAK SPACE
            case '\u205F': //MEDIUM MATHEMATICAL SPACE
            case '\u3000': //IDEOGRAPHIC SPACE
            case '\u2028': //LINE SEPARATOR
            case '\u2029': //PARAGRAPH SEPARATOR
            case '\u0009': //[ASCII Tab]
            case '\u000A': //[ASCII Line Feed]
            case '\u000B': //[ASCII Vertical Tab]
            case '\u000C': //[ASCII Form Feed]
            case '\u000D': //[ASCII Carriage Return]
            case '\u0085': //NEXT LINE
                if (lastWasWS == false) //Added line
                {
                    src[dstIdx++] = ch; //Added line
                    lastWasWS = true; //Added line
                }
            continue;
            default:
                lastWasWS = false; //Added line 
                src[dstIdx++] = ch;
                break;
        }
    }
    return new string(src, 0, dstIdx);
}

static readonly Regex MultipleSpaces =
    new Regex(@" {2,}", RegexOptions.Compiled);

//Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
static string SplitAndJoinOnSpace(string input)
{
    string[] split = input.Split(new char[] { ' '}, StringSplitOptions.RemoveEmptyEntries);
    return string.Join(" ", split);
}

//StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
public static string user214147(string S)
{
    string s = S.Trim();
    bool iswhite = false;
    int iwhite;
    int sLength = s.Length;
    StringBuilder sb = new StringBuilder(sLength);
    foreach (char c in s.ToCharArray())
    {
        if (Char.IsWhiteSpace(c))
        {
            if (iswhite)
            {
                //Continuing whitespace ignore it.
                continue;
            }
            else
            {
                //New WhiteSpace

                //Replace whitespace with a single space.
                sb.Append(" ");
                //Set iswhite to True and any following whitespace will be ignored
                iswhite = true;
            }
        }
        else
        {
            sb.Append(c.ToString());
            //reset iswhitespace to false
            iswhite = false;
        }
    }
    return sb.ToString();
}

//StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
public static string fubo(this string Value)
{
    StringBuilder sbOut = new StringBuilder();
    if (!string.IsNullOrEmpty(Value))
    {
        bool IsWhiteSpace = false;
        for (int i = 0; i < Value.Length; i++)
        {
            if (char.IsWhiteSpace(Value[i])) //Comparison with WhiteSpace
            {
                if (!IsWhiteSpace) //Comparison with previous Char
                {
                    sbOut.Append(Value[i]);
                    IsWhiteSpace = true;
                }
            }
            else
            {
                IsWhiteSpace = false;
                sbOut.Append(Value[i]);
            }
        }
    }
    return sbOut.ToString();
}

//David S. 2013 (https://stackoverflow.com/a/16035044/2352507)
public static String SingleSpacedTrim(String inString)
{
    StringBuilder sb = new StringBuilder();
    Boolean inBlanks = false;
    foreach (Char c in inString)
    {
        switch (c)
        {
            case '\r':
            case '\n':
            case '\t':
            case ' ':
                if (!inBlanks)
                {
                    inBlanks = true;
                    sb.Append(' ');
                }
                continue;
            default:
                inBlanks = false;
                sb.Append(c);
                break;
        }
    }
    return sb.ToString().Trim();
}

/// <summary>
/// We want to run this item with max priory to lower the odds of
/// the OS from doing program context switches in the middle of our code. 
/// source:https://stackoverflow.com/a/16157458 
/// </summary>
/// <returns>random seed</returns>
private static long ConfigProgramForBenchmarking()
{
    //prevent the JIT Compiler from optimizing Fkt calls away
    long seed = Environment.TickCount;
    //use the second Core/Processor for the test
    Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
    //prevent "Normal" Processes from interrupting Threads
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
    //prevent "Normal" Threads from interrupting this thread
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    return seed;
}

}

Notas de referencia: modo de lanzamiento, sin depurador adjunto, procesador i7, promedio de 4 ejecuciones, solo se probaron cadenas cortas

Sunsetquest
fuente
1
¡Es bueno ver mi artículo mencionado aquí! (Soy Felipe Machado) ¡Estoy a punto de actualizarlo usando una herramienta de referencia adecuada llamada BenchmarkDotNet! Intentaré configurar ejecuciones en todos los tiempos de ejecución (ahora que tenemos DOT NET CORE y similares ...
Loudenvier
1
@Loudenvier - Buen trabajo en esto. ¡El tuyo fue el más rápido en casi un 400%! .Net Core es como un aumento de rendimiento gratuito del 150-200%. Se acerca al rendimiento de C ++ pero es mucho más fácil de codificar. Gracias por el comentario.
Sunsetquest
Esto solo incluye espacios, no otros caracteres de espacios en blanco. Tal vez desee char.IsWhiteSpace (ch) en lugar de src [i] == '\ u0020'. Noto que esto ha sido editado por la comunidad. ¿Lo estropearon?
Paloma malvada
3

Estoy compartiendo lo que uso, porque parece que se me ocurrió algo diferente. He estado usando esto por un tiempo y es lo suficientemente rápido para mí. No estoy seguro de cómo se compara con los demás. Lo uso en un escritor de archivos delimitados y ejecuto tablas de datos grandes un campo a la vez a través de él.

    public static string NormalizeWhiteSpace(string S)
    {
        string s = S.Trim();
        bool iswhite = false;
        int iwhite;
        int sLength = s.Length;
        StringBuilder sb = new StringBuilder(sLength);
        foreach(char c in s.ToCharArray())
        {
            if(Char.IsWhiteSpace(c))
            {
                if (iswhite)
                {
                    //Continuing whitespace ignore it.
                    continue;
                }
                else
                {
                    //New WhiteSpace

                    //Replace whitespace with a single space.
                    sb.Append(" ");
                    //Set iswhite to True and any following whitespace will be ignored
                    iswhite = true;
                }  
            }
            else
            {
                sb.Append(c.ToString());
                //reset iswhitespace to false
                iswhite = false;
            }
        }
        return sb.ToString();
    }
usuario214147
fuente
2

Usando el programa de prueba que publicó Jon Skeet, traté de ver si podía hacer que un bucle escrito a mano se ejecutara más rápido.
Puedo vencer a NormalizeWithSplitAndJoin cada vez, pero solo vencer a NormalizeWithRegex con entradas de 1000, 5.

static string NormalizeWithLoop(string input)
{
    StringBuilder output = new StringBuilder(input.Length);

    char lastChar = '*';  // anything other then space 
    for (int i = 0; i < input.Length; i++)
    {
        char thisChar = input[i];
        if (!(lastChar == ' ' && thisChar == ' '))
            output.Append(thisChar);

        lastChar = thisChar;
    }

    return output.ToString();
}

No he mirado el código de máquina que produce el jitter, sin embargo, espero que el problema sea el tiempo que tarda la llamada a StringBuilder.Append () y para hacerlo mucho mejor necesitaría el uso de código inseguro.

Entonces, Regex.Replace () es muy rápido y difícil de superar.

Ian Ringrose
fuente
2

VB.NET

Linha.Split(" ").ToList().Where(Function(x) x <> " ").ToArray

C#

Linha.Split(" ").ToList().Where(x => x != " ").ToArray();

Disfrute del poder de LINQ = D

Patryk Moura
fuente
¡Exactamente! Para mí, este también es el enfoque más elegante. Entonces, para el registro, en C # sería:string.Join(" ", myString.Split(' ').Where(s => s != " ").ToArray())
Efraín
1
Mejora menor en la Splitcaptura de todos los espacios en blanco y eliminar la Wherecláusula:myString.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)
David
1
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");
Michael D.
fuente
Esto reemplaza todos los caracteres que no son palabras por espacio. Por lo tanto, también reemplazaría cosas como corchetes y comillas, etc., que podrían no ser lo que desea.
Herman
0

Solución más pequeña:

var regExp = / \ s + / g, newString = oldString.replace (regExp, '');


fuente
0

Puedes probar esto:

    /// <summary>
    /// Remove all extra spaces and tabs between words in the specified string!
    /// </summary>
    /// <param name="str">The specified string.</param>
    public static string RemoveExtraSpaces(string str)
    {
        str = str.Trim();
        StringBuilder sb = new StringBuilder();
        bool space = false;
        foreach (char c in str)
        {
            if (char.IsWhiteSpace(c) || c == (char)9) { space = true; }
            else { if (space) { sb.Append(' '); }; sb.Append(c); space = false; };
        }
        return sb.ToString();
    }
LL99
fuente
0

Los grupos de reemplazo proporcionan un enfoque de implementación que resuelve el reemplazo de varios caracteres de espacio en blanco con el mismo único:

    public static void WhiteSpaceReduce()
    {
        string t1 = "a b   c d";
        string t2 = "a b\n\nc\nd";

        Regex whiteReduce = new Regex(@"(?<firstWS>\s)(?<repeatedWS>\k<firstWS>+)");
        Console.WriteLine("{0}", t1);
        //Console.WriteLine("{0}", whiteReduce.Replace(t1, x => x.Value.Substring(0, 1))); 
        Console.WriteLine("{0}", whiteReduce.Replace(t1, @"${firstWS}"));
        Console.WriteLine("\nNext example ---------");
        Console.WriteLine("{0}", t2);
        Console.WriteLine("{0}", whiteReduce.Replace(t2, @"${firstWS}"));
        Console.WriteLine();
    }

Tenga en cuenta que el segundo ejemplo se mantiene solo \n mientras que la respuesta aceptada reemplazaría el final de la línea con un espacio.

Si necesita reemplazar cualquier combinación de caracteres de espacio en blanco con el primero, simplemente elimine la referencia \kinversa del patrón.

Dan
fuente
0

El uso de expresiones regulares, para reemplazar 2 o más espacios en blanco con un solo espacio, también es una buena solución.

Estamos usando el patrón de expresiones regulares como " \ s + ".

  • \ s coincide con un espacio, tabulación, nueva línea, retorno de carro, avance de página o tabulación vertical.

  • '+' dice una o más ocurrencias.

Ejemplo de regex

String blogName = "  Sourav .  Pal.   "

 String nameWithProperSpacing = blogName.replaceAll("\\s+", " ");   
System.out.println( nameWithProperSpacing );
sourav pal
fuente
-1

No hay forma integrada de hacer esto. Puedes probar esto:

private static readonly char[] whitespace = new char[] { ' ', '\n', '\t', '\r', '\f', '\v' };
public static string Normalize(string source)
{
   return String.Join(" ", source.Split(whitespace, StringSplitOptions.RemoveEmptyEntries));
}

Esto eliminará los espacios en blanco iniciales y finales, así como colapsará cualquier espacio en blanco interno a un solo carácter de espacio en blanco. Si realmente solo desea contraer espacios, entonces las soluciones que usan una expresión regular son mejores; de lo contrario, esta solución es mejor. (Vea el análisis realizado por Jon Skeet).

Scott Dorman
fuente
7
Si la expresión regular se compila y almacena en caché, no estoy seguro de que tenga más gastos generales que dividir y unir, lo que podría crear un montón de cadenas de basura intermedias. ¿Ha realizado evaluaciones comparativas cuidadosas de ambos enfoques antes de asumir que su camino es más rápido?
Jon Skeet
1
los espacios en blanco no están declarados aquí
Tim Hoolihan
3
Hablando de gastos generales, ¿por qué diablos estás llamando source.ToCharArray()y luego tirando el resultado?
Jon Skeet
2
Y llamar ToCharArray()al resultado de string.Join, solo para crear una nueva cadena ... wow, que esté en una publicación quejándose de sobrecarga es simplemente notable. -1.
Jon Skeet
1
Ah, y suponiendo que whitespacesea ​​así new char[] { ' ' }, esto dará un resultado incorrecto si la cadena de entrada comienza o termina con un espacio.
Jon Skeet