Come sostituire più spazi bianchi con uno spazio bianco

Diciamo che ho una stringa come:

"Hello how are you doing?" 

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

Quindi vorrei ottenere:

 "Hello how are you doing?" 

So che potrei usare regex o chiamare

 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?

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

Questa domanda non è semplice come hanno fatto altri poster (e come inizialmente credevo che fosse) – perché la domanda non è precisa come deve essere.

C’è una differenza tra “spazio” e “spazio bianco”. Se intendi solo spazi, dovresti usare un’espressione regolare di " {2,}" . Se intendi uno spazio bianco, è diverso. Dovrebbero essere convertiti tutti gli spazi in spazi? Cosa dovrebbe accadere allo spazio all’inizio e alla fine?

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

Si noti che la correttezza è quasi sempre più importante delle prestazioni. Il fatto che la soluzione Split / Join rimuova gli spazi bianchi iniziali o finali (anche solo spazi singoli) non è corretto per quanto riguarda i requisiti specificati (che possono 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("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); } } 

Alcune corse di prova:

 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 con 1.0 che è il migliore.

Ciò dimostra che in alcuni casi (compreso questo) un’espressione regolare può sovraperformare la soluzione Split / Join, a volte con un margine molto significativo.

Tuttavia, se si passa a un requisito di “tutti gli spazi bianchi”, Split / Join sembra vincere. Come spesso accade, il diavolo è nei dettagli …

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ò loop per sempre. Qualcuno ha voglia di indovinare perché? (Mi sono imbattuto in questo solo quando è stato chiesto come una domanda sui newsgroup qualche anno fa … qualcuno ci si è imbattuto in un problema.)

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

Cambiarlo in questo:

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

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

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

Sto condividendo quello che uso, perché sembra che io abbia inventato qualcosa di diverso. Ho usato questo per un po ‘ed è abbastanza veloce per me. Non sono sicuro di come si accumula contro gli altri. Lo utilizzo in un file writer delimitato ed eseguo grandi datatables 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(); } 

Usando il programma di test pubblicato da Jon Skeet, ho provato a vedere se potevo ottenere un ciclo scritto a mano per correre più veloce.
Posso battere NormalizeWithSplitAndJoin ogni volta, ma solo battere NormalizeWithRegex con ingressi 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 prevedo 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 !!

Un dispositivo di rimozione dello spazio bianco extra veloce … Questo è il più veloce ed è basato 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 parametri di riferimento ...

InPlaceCharArraySpaceOnly di Felipe Machado su CodeProject 2015 e modificato da Sunsetquest per la rimozione di più spazi. Tempo: 3,75 zecche

InPlaceCharArray di Felipe Machado 2015 e leggermente modificato da Sunsetquest per la rimozione di più spazi. Tempo 6,50 zecche (supporta anche le tabs)

SplitAndJoinOnSpace di Jon Skeet . Tempo: 13.25 zecche

StringBuilder di fubo Tempo: 13,5 zecche (supporta anche le tabs)

Regex con la compilazione di Jon Skeet . Tempo: 17 zecche

StringBuilder di David S 2013 Durata: 30,5 zecche

Regex con non-compilato da Brandon Tempo: 63.25 Zecche

StringBuilder by user214147 Ora: 77,125 zecche

Regex con Tim Hoolihan non compilato . Tempo: 147.25 Tick

Il codice di riferimento ...

 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(); } ///  /// 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 ///  /// random seed 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 sul benchmark: Release Mode, no-debugger allegato, processore i7, avg di 4 esecuzioni, solo stringhe corte testate

VB.NET

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

C #

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

Goditi la potenza di LINQ = D

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

La soluzione più piccola:

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

Non vi è alcun modo 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)); } 

Questo rimuoverà il whitespce iniziale e finale e collasserà tutti gli spazi bianchi interni in un singolo carattere di spaziatura. Se vuoi veramente solo ridurre gli spazi, allora le soluzioni che usano un’espressione regolare sono migliori; altrimenti questa soluzione è migliore. (Vedi l’ analisi fatta da Jon Skeet.)