Angenommen, ich habe eine Zeichenfolge wie:
"Hello how are you doing?"
Ich möchte eine Funktion, die mehrere Räume in einen Raum verwandelt.
Also würde ich bekommen:
"Hello how are you doing?"
Ich weiß, ich könnte Regex verwenden oder anrufen
string s = "Hello how are you doing?".replace(" "," ");
Aber ich müsste es mehrmals aufrufen, um sicherzustellen, dass alle aufeinander folgenden Leerzeichen durch nur ein Leerzeichen ersetzt werden.
Gibt es dafür bereits eine eingebaute Methode?
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
Diese Frage ist nicht so einfach wie andere Plakate es sich vorgestellt haben (und wie ich ursprünglich angenommen habe) - weil die Frage nicht so genau ist, wie sie sein muss.
Es gibt einen Unterschied zwischen "Leerzeichen" und "Leerzeichen". Wenn Sie nur Leerzeichen meinen, sollten Sie eine Regex von " {2,}"
. Wenn Sie ein Leerzeichen meinen, ist das eine andere Sache. Sollten alle Leerzeichen in Leerzeichen umgewandelt werden? Was soll am Anfang und am Ende mit dem Weltraum geschehen?
Für den folgenden Benchmark habe ich angenommen, dass Sie sich nur um Leerzeichen kümmern und auch am Anfang und Ende nichts mit einzelnen Leerzeichen anfangen möchten.
Beachten Sie, dass Korrektheit fast immer wichtiger ist als Leistung. Die Tatsache, dass die Split/Join-Lösung alle führenden/nachfolgenden Leerzeichen (auch nur einzelne Leerzeichen) entfernt, entspricht nicht Ihren Anforderungen (was natürlich unvollständig sein kann).
Der Benchmark verwendet 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);
}
}
Einige Testläufe:
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
Hier ist die erste Zahl die Anzahl der Iterationen, die zweite die benötigte Zeit und die dritte eine skalierte Punktzahl, wobei 1,0 die beste ist.
Dies zeigt, dass ein regulärer Ausdruck zumindest in einigen Fällen (einschließlich dieses) die Split/Join-Lösung übertreffen kann , manchmal mit einem sehr großen Abstand.
Wenn Sie jedoch zu einer "All Whitespace" -Anforderung wechseln, scheint Split/Join zu gewinnen . Wie so oft steckt der Teufel im Detail ...
Während die vorhandenen Antworten in Ordnung sind, möchte ich auf einen Ansatz hinweisen, der nicht funktioniert:
public static string DontUseThisToCollapseSpaces(string text)
{
while (text.IndexOf(" ") != -1)
{
text = text.Replace(" ", " ");
}
return text;
}
Dies kann eine Endlosschleife sein. Möchte jemand raten, warum? (Ich bin auf diese Frage erst vor ein paar Jahren als Newsgroup-Frage gestoßen ... tatsächlich ist jemand darauf gestoßen.)
Ein regulärer Ausdruck wäre der einfachste Weg. Wenn Sie den regulären Ausdruck richtig schreiben, brauchen Sie nicht mehrere Aufrufe.
Ändern Sie es zu diesem:
string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " ");
Hier ist die Lösung, mit der ich arbeite. Ohne RegEx und 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();
}
also kannst du:
string cleanedString = dirtyString.TrimWhiteSpace();
Wie bereits erwähnt, kann dies leicht durch einen regulären Ausdruck erfolgen. Ich möchte nur hinzufügen, dass Sie möglicherweise ein .trim () hinzufügen möchten, um führende/nachfolgende Leerzeichen zu entfernen.
Ich teile, was ich benutze, weil mir anscheinend etwas anderes eingefallen ist. Ich benutze es schon eine Weile und es ist schnell genug für mich. Ich bin mir nicht sicher, wie es sich gegen die anderen schlägt. Ich verwende es in einem begrenzten Datei-Schreiber und führe große Datentabellen Feld für Feld durch.
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();
}
Ein schneller zusätzlicher Whitespace-Entferner ... Dies ist der schnellste und basiert auf Felipe Machados In-Place-Kopie.
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);
}
Die Benchmarks ...
InPlaceCharArraySpaceOnly von Felipe Machado in CodeProject 2015 und geändert von Sunsetquest zum Entfernen mehrerer Leerzeichen. Zeit: 3,75 Ticks
InPlaceCharArray von Felipe Machado 2015 und von Sunsetquest für die Entfernung mehrerer Leerzeichen leicht modifiziert. Zeit 6.50 Ticks (unterstützt auch Tabs)
SplitAndJoinOnSpace von Jon Skeet . Zeit: 13,25 Ticks
StringBuilder von fubo Zeit: 13,5 Ticks (unterstützt auch Tabs)
Regex mit kompilieren von Jon Skeet . Zeit: 17 Ticks
StringBuilder von David S 2013 Zeit: 30,5 Ticks
Regex mit nicht kompilieren von Brandon Zeit: 63,25 Ticks
StringBuilder von ser214147 Zeit: 77.125 Ticks
Regex mit non-compile Tim Hoolihan Zeit: 147,25 Ticks
Der Benchmark-Code ...
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;
}
}
Benchmark-Hinweise: Release-Modus, No-Debugger, i7-Prozessor, durchschnittlich 4 Läufe, nur kurze Strings getestet
VB.NET
Linha.Split(" ").ToList().Where(Function(x) x <> " ").ToArray
C #
Linha.Split(" ").ToList().Where(x => x != " ").ToArray();
Genießen Sie die Kraft von LINQ = D
Mit dem Testprogramm, das Jon Skeet gepostet hat, habe ich versucht, eine handgeschriebene Schleife schneller laufen zu lassen.
Ich kann jedes Mal NormalizeWithSplitAndJoin schlagen, aber nur NormalizeWithRegex mit Eingaben von 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();
}
Ich habe mir den Maschinencode, den der Jitter erzeugt, nicht angesehen, aber ich gehe davon aus, dass das Problem die Zeit ist, die der Aufruf von StringBuilder.Append () benötigt, und für eine bessere Ausführung wäre die Verwendung von unsicherem Code erforderlich.
Also Regex.Replace () ist sehr schnell und schwer zu schlagen !!
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");
Kleinste Lösung:
var regExp =/\ s +/g, newString = oldString.replace (regExp, '');
Sie können dies versuchen:
/// <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();
}