Pregunta Cómo reemplazar múltiples espacios en blanco con un espacio en blanco


Digamos que tengo una cadena como:

"Hello     how are   you           doing?"

Me gustaría una función que convierta múltiples espacios en un 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 por uno solo.

¿Ya hay un método integrado para esto?


95
2017-08-14 19:53


origen


Respuestas:


string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");

171
2017-08-14 19:57



Esta pregunta no es tan simple como lo han hecho otros carteles (y como originalmente pensé que era), porque la pregunta no es tan precisa como debe ser.

Hay una diferencia entre "espacio" y "espacio en blanco". Si tu solamente espacios medios, entonces deberías usar una expresión regular de " {2,}". Si te refieres a alguna espacio en blanco, esa es una cuestión diferente. Debería todas el espacio en blanco se convierte en espacios? ¿Qué debería pasarle al espacio al principio y al final?

Para el punto de referencia a continuación, he supuesto que solo le interesan los espacios, y que no desea hacer nada en espacios individuales, ni siquiera al principio ni 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 individuales) es incorrecto en cuanto a sus requisitos específicos (que pueden estar incompletos, por supuesto).

Los usos de referencia 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 empleado y el tercero es un puntaje escalado con 1.0 siendo el mejor.

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

Sin embargo, si cambia a un requisito de "todos los espacios en blanco", entonces Split / Join hace parece que gana Como suele ser el caso, el diablo está en el detalle ...


52
2017-08-14 21:05



Si bien las respuestas existentes son buenas, me gustaría señalar un enfoque que no lo hace trabajo:

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

Esto puede repetir para siempre. ¿Alguien quiere adivinar por qué? (Solo me encontré con esto cuando se lo preguntaron como una pregunta del grupo de noticias hace unos años ... alguien en realidad lo encontró como un problema).


18
2017-08-14 20:03



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

Cambiarlo a esto:

string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " "); 

16
2017-08-14 19:58



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 del espacio en blanco inicial / final.


4
2017-08-14 20:19



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

4
2017-12-16 10:23



Estoy compartiendo lo que uso, porque parece que he encontrado 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 delimitado y ejecuto grandes tablas de datos 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();
    }

3
2018-01-28 17:53



Usando el programa de prueba que publicó Jon Skeet, traté de ver si podía obtener un ciclo escrito a mano para correr más rápido.
Puedo vencer a NormalizeWithSplitAndJoin cada vez, pero solo venzo 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 examinado el código de máquina que produce la fluctuación, sin embargo, creo que el problema es el tiempo que toma la llamada a StringBuilder.Append () y para hacerlo mucho mejor necesitaría el uso de un código inseguro.

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


2
2017-12-20 14:50



Un removedor de espacio en blanco extra rápido ... Este es el más rápido y está basado en la copia local 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 la eliminación de múltiples espacios. Tiempo: 3.75 Ticks

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

SplitAndJoinOnSpace por Jon Skeet. Tiempo: 13.25 Ticks

StringBuilder por fubo Tiempo: 13.5 Ticks (admite pestañas también)

Regex con compilación por Jon Skeet. Tiempo: 17 Ticks

StringBuilder por David S 2013 Tiempo: 30.5 Ticks

Regex con no compilación por Brandon Tiempo: 63.25 Ticks

StringBuilder por usuario214147 Tiempo: 77.125 Ticks

Regex con no compilación Tim Hoolihan Tiempo: 147.25 Ticks

El código de Benchmark ...

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 liberación, sin depurador conectado, procesador i7, promedio de 4 carreras, solo cadenas cortas probadas


2
2017-10-05 05:02



VB.NET

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

DO#

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

Disfruta el poder de LINQ = D


2
2017-10-14 15:43