Come sostituire più spazi bianchi con uno spazio bianco


108

Diciamo che ho una stringa come:

"Hello     how are   you           doing?"

Vorrei una funzione che trasformi più spazi in uno spazio.

Quindi otterrei:

"Hello how are you doing?"

So che potrei usare regex o call

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

Ma dovrei chiamarlo più volte per assicurarmi che tutti gli spazi bianchi sequenziali vengano sostituiti con uno solo.

Esiste già un metodo integrato per questo?


Puoi chiarire: hai a che fare solo con spazi o "tutti" gli spazi bianchi?
Jon Skeet

E vuoi che gli spazi bianchi non spaziali vengano convertiti in spazi?
Jon Skeet

Intendevo solo che tutti gli spazi bianchi in serie dovrebbero essere al massimo 1
Matt


2 cose da considerare: 1. char.IsWhiteSpace include ritorno a capo, avanzamento riga, ecc. 2. 'whitespace' è probabilmente testato più accuratamente con Char.GetUnicodeCategory (ch) = Globalization.UnicodeCategory.SpaceSeparator
smirkingman

Risposte:


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

40
imo, evitare regex se ti
senti a

8
Se l'applicazione non è critica in termini di tempo, può permettersi 1 microsecondo di sovraccarico di elaborazione.
Daniel

16
Nota che "\ s" non solo sostituisce gli spazi bianchi, ma anche i nuovi caratteri di riga.
Bart Kiers

12
buona cattura, se vuoi solo gli spazi cambia lo schema in "[] ​​+"
Tim Hoolihan

9
Non dovresti usare "{2,}" invece di "+" per evitare di sostituire singoli spazi bianchi?
angularsen

52

Questa domanda non è così semplice come altri poster hanno pensato che fosse (e come inizialmente credevo che fosse) - perché la domanda non è abbastanza precisa come dovrebbe essere.

C'è una differenza tra "spazio" e "spazio bianco". Se intendi solo spazi, dovresti usare una regex di " {2,}". Se intendi uno spazio bianco, è una questione diversa. Tutti gli spazi bianchi dovrebbero essere convertiti in spazi? Cosa dovrebbe accadere allo spazio all'inizio e alla fine?

Per il benchmark di seguito, ho assunto che ti interessi solo degli spazi e non vuoi fare nulla per singoli spazi, anche all'inizio e alla fine.

Nota che la correttezza è quasi sempre più importante delle prestazioni. Il fatto che la soluzione Dividi / Unisci rimuova qualsiasi spazio bianco iniziale / finale (anche solo singoli spazi) non è corretto per quanto riguarda i requisiti specificati (che potrebbero essere incompleti, ovviamente).

Il benchmark utilizza 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);
    }
}

Alcuni test eseguiti:

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

Qui il primo numero è il numero di iterazioni, il secondo è il tempo impiegato e il terzo è un punteggio in scala dove 1.0 è il migliore.

Ciò mostra che in almeno alcuni casi (incluso questo) un'espressione regolare può sovraperformare la soluzione Dividi / Unisci, a volte con un margine molto significativo.

Tuttavia, se si passa a un requisito "tutti gli spazi", allora Split / Join non appare per vincere. Come spesso accade, il diavolo è nei dettagli ...


1
Ottima analisi. Quindi sembra che entrambi avessimo ragione a vari livelli. Il codice nella mia risposta è stato preso da una funzione più grande che ha la capacità di normalizzare tutti gli spazi e / o i caratteri di controllo dall'interno di una stringa e dall'inizio e dalla fine.
Scott Dorman

1
Con solo i caratteri di spazio bianco che hai specificato, nella maggior parte dei miei test la regex e Split / Join erano all'incirca uguali: S / J ha avuto un piccolo, minuscolo vantaggio, a scapito della correttezza e della complessità. Per questi motivi, normalmente preferirei la regex. Non fraintendetemi: sono lontano dall'essere un fanboy di regex, ma non mi piace scrivere codice più complesso per motivi di prestazioni senza prima testare veramente le prestazioni.
Jon Skeet

NormalizeWithSplitAndJoin creerà molta più spazzatura, è difficile dire se un problema reale verrà colpito più tempo GC rispetto al banchmark.
Ian Ringrose

@IanRingrose Che tipo di spazzatura si può creare?
Dronz

18

Un normale espresso sarebbe il modo più semplice. Se scrivi la regex nel modo corretto, non avrai bisogno di più chiamate.

Cambialo in questo:

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

Il mio unico problema @"\s{2,}"è che non riesce a sostituire le singole schede e altri caratteri di spazio Unicode con uno spazio. Se hai intenzione di sostituire 2 schede con uno spazio, probabilmente dovresti sostituire 1 scheda con uno spazio. @"\s+"lo farà per te.
David Specht,

17

Mentre le risposte esistenti vanno bene, vorrei sottolineare un approccio che non funziona:

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

Questo può andare in loop per sempre. Qualcuno vuole indovinare il motivo? (Mi sono imbattuto in questo solo quando è stato chiesto come domanda di un newsgroup alcuni anni fa ... qualcuno lo ha riscontrato come un problema.)


Penso di ricordare questa domanda che mi è stata posta da tempo su SO. IndexOf ignora alcuni caratteri che Sostituisci non fa. Quindi il doppio spazio era sempre lì, ma mai rimosso.
Brandon

19
È perché IndexOf ignora alcuni caratteri Unicode, il colpevole specifico in questo caso è un carattere asiatico iirc. Hmm, zero-width non-joiner secondo Google.
ahawker

L'ho imparato a mie spese
Antonio Bakula

Ho imparato a mie spese. Soprattutto con due Zero Width Non Joiners (\ u200C \ u200C). IndexOf restituisce l'indice di questo "doppio spazio", ma Replace non lo sostituisce. Penso che sia perché per IndexOf, è necessario specificare StringComparsion (Ordinal) per comportarsi allo stesso modo di Replace. In questo modo, nessuno di questi due troverà "doppi spazi". Ulteriori informazioni su StringComparsion docs.microsoft.com/en-us/dotnet/api/…
Martin Brabec

4

Come già sottolineato, questo può essere fatto facilmente da un'espressione regolare. Aggiungerò solo che potresti voler aggiungere un .trim () a quello per sbarazzarti degli spazi bianchi iniziali / finali.


4

Ecco la soluzione con cui lavoro. Senza RegEx e 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();
}

così puoi:

string cleanedString = dirtyString.TrimWhiteSpace();

4

Un veloce dispositivo di rimozione degli spazi bianchi extra ... Questo è il più veloce e si basa sulla copia sul posto di 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);
}

I benchmark ...

InPlaceCharArraySpaceOnly di Felipe Machado su CodeProject 2015 e modificato da Sunsetquest per la rimozione multi-spazio. Tempo: 3,75 tick

InPlaceCharArray di Felipe Machado 2015 e leggermente modificato da Sunsetquest per la rimozione multi-spazio. Ora 6,50 tick (supporta anche le schede)

SplitAndJoinOnSpace di Jon Skeet . Tempo: 13.25 tick

StringBuilder di fubo Time: 13,5 tick (supporta anche le schede)

Regex con compilazione di Jon Skeet . Tempo: 17 tick

StringBuilder di David S 2013 Tempo: 30,5 tick

Regex con non compilazione di Brandon Time: 63.25 tick

StringBuilder di user214147 Tempo: 77.125 tick

Regex con Tim Hoolihan non compilabile Time: 147.25 tick

Il codice 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;
}

}

Note di benchmark: modalità di rilascio, nessun debugger collegato, processore i7, media di 4 sessioni, testate solo stringhe brevi


1
Bello vedere il mio articolo citato qui! (Sono Felipe Machado) Sto per aggiornarlo utilizzando un apposito strumento di benchmark chiamato BenchmarkDotNet! Proverò a impostare le corse in tutti i runtime (ora che abbiamo DOT NET CORE e simili ...
Loudenvier

1
@Loudenvier - Bel lavoro su questo. Il tuo è stato il più veloce di quasi il 400%! .Net Core è come un aumento gratuito delle prestazioni del 150-200%. Si sta avvicinando alle prestazioni di c ++ ma è molto più facile da programmare. Grazie per il commento.
Sunsetquest

Questo fa solo spazi, non altri caratteri di spazio bianco. Forse vuoi char.IsWhiteSpace (ch) invece di src [i] == '\ u0020'. Ho notato che è stato modificato dalla comunità. L'hanno rovinato?
Evil Pigeon

3

Condivido quello che uso, perché sembra che abbia escogitato qualcosa di diverso. Lo uso da un po 'di tempo ed è abbastanza veloce per me. Non sono sicuro di come si sovrapponga agli altri. Lo uso in un programma di scrittura di file delimitato ed eseguo dati di grandi dimensioni un campo alla volta attraverso di esso.

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

2

Utilizzando il programma di test pubblicato da Jon Skeet, ho provato a vedere se riuscivo a far funzionare più velocemente un ciclo scritto a mano.
Posso battere NormalizeWithSplitAndJoin ogni volta, ma battere NormalizeWithRegex solo con input di 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();
}

Non ho esaminato il codice macchina prodotto dal jitter, tuttavia mi aspetto che il problema sia il tempo impiegato dalla chiamata a StringBuilder.Append () e per fare molto meglio sarebbe necessario l'uso di codice non sicuro.

Quindi Regex.Replace () è molto veloce e difficile da battere !!


2

VB.NET

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

C #

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

Goditi la potenza di LINQ = D


Esattamente! Anche per me questo è l'approccio più elegante. Quindi, per la cronaca, in C # sarebbe:string.Join(" ", myString.Split(' ').Where(s => s != " ").ToArray())
Efrain

1
Minore miglioramento sul Splitper catturare tutti gli spazi bianchi e rimuovere la Whereclausola:myString.Split(null as char[], StringSplitOptions.RemoveEmptyEntries)
David

1
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");

Sostituisce tutti i caratteri non verbali con uno spazio. Quindi sostituirebbe anche cose come parentesi e virgolette ecc., Che potrebbero non essere ciò che desideri.
Herman

0

Soluzione più piccola:

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


0

Puoi provare questo:

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

0

I gruppi di sostituzione forniscono un approccio implementatore che risolve la sostituzione di più caratteri di spazi vuoti con lo stesso singolo:

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

Si prega di notare che il secondo esempio rimane single \n mentre la risposta accettata sostituirebbe la fine della riga con uno spazio.

Se è necessario sostituire qualsiasi combinazione di caratteri di spazio bianco con il primo, è sufficiente rimuovere il riferimento a ritroso \kdal modello.


0

Anche l'uso di espressioni regolari, per sostituire 2 o più spazi bianchi con uno spazio singolo, è una buona soluzione.

Stiamo usando il pattern regex come " \ s + ".

  • \ s corrisponde a uno spazio, tabulazione, nuova riga, ritorno a capo, avanzamento modulo o tabulazione verticale.

  • "+" dice una o più occorrenze.

Esempio di espressione regolare

String blogName = "  Sourav .  Pal.   "

 String nameWithProperSpacing = blogName.replaceAll("\\s+", " ");   
System.out.println( nameWithProperSpacing );

-1

Non c'è modo integrato per farlo. Puoi provare questo:

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

Ciò rimuoverà lo spazio bianco iniziale e finale e comprimerà qualsiasi spazio bianco interno in un singolo carattere di spazio bianco. Se in realtà vuoi solo comprimere gli spazi, le soluzioni che utilizzano un'espressione regolare sono migliori; altrimenti questa soluzione è migliore. (Vedi l' analisi fatta da Jon Skeet.)


7
Se l'espressione regolare viene compilata e memorizzata nella cache, non sono sicuro che abbia più overhead della divisione e dell'unione , il che potrebbe creare un sacco di stringhe di immondizia intermedie. Hai eseguito accurati benchmark di entrambi gli approcci prima di presumere che la tua strada sia più veloce?
Jon Skeet

1
lo spazio bianco non è dichiarato qui
Tim Hoolihan

3
A proposito di overhead, perché diavolo chiami source.ToCharArray()e poi butti via il risultato?
Jon Skeet

2
E invocando ToCharArray()il risultato di string.Join, solo per creare una nuova stringa ... wow, per questo essere in un post che si lamenta dell'overhead è semplicemente notevole. -1.
Jon Skeet

1
Oh, e supponendo che whitespacesia new char[] { ' ' }, questo darà il risultato sbagliato se la stringa di input inizia o finisce con uno spazio.
Jon Skeet
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.