Was ist der schnellste Weg, um zusätzliche Leerzeichen in ein Leerzeichen zu ersetzen?
z.B.
von
foo bar
bis
foo bar
Der schnellste Weg Durchlaufen Sie den String und erstellen Sie eine zweite Kopie in einer StringBuilder
Zeichenweise, wobei Sie für jede Gruppe von Leerzeichen nur ein Leerzeichen kopieren.
Die einfacher zu Replace
-Varianten generieren eine Eimerladung mit zusätzlichen Zeichenfolgen (oder verschwenden Zeit beim Erstellen des regulären DFA).
Mit Vergleichsergebnissen bearbeiten:
Mit http://ideone.com/h6pw3 , mit n = 50 (musste es auf Ideone reduzieren, da es so lange gedauert hat, dass sie meinen Prozess beenden mussten), bekomme ich:
Regex: 7771 ms.
Stringbuilder: 894ms.
Was tatsächlich so ist wie erwartet, ist Regex
für etwas so einfaches schrecklich ineffizient.
Sie können einen Regex verwenden:
static readonly Regex trimmer = new Regex(@"\s\s+");
s = trimmer.Replace(s, " ");
Für zusätzliche Leistung übergeben Sie RegexOptions.Compiled
.
Ein bisschen spät, aber ich habe ein paar Benchmarks durchgeführt, um den schnellsten Weg zu finden, um zusätzliche Whitespaces zu entfernen. Wenn es schnellere Antworten gibt, würde ich sie gerne hinzufügen.
Ergebnisse:
Code:
public class RemoveExtraWhitespaces
{
public static string WithRegex(string text)
{
return Regex.Replace(text, @"\s+", " ");
}
public static string WithRegexCompiled(Regex compiledRegex, string text)
{
return compiledRegex.Replace(text, " ");
}
public static string NormalizeWhiteSpace(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
int current = 0;
char[] output = new char[input.Length];
bool skipped = false;
foreach (char c in input.ToCharArray())
{
if (char.IsWhiteSpace(c))
{
if (!skipped)
{
if (current > 0)
output[current++] = ' ';
skipped = true;
}
}
else
{
skipped = false;
output[current++] = c;
}
}
return new string(output, 0, current);
}
public static string NormalizeWhiteSpaceForLoop(string input)
{
int len = input.Length,
index = 0,
i = 0;
var src = input.ToCharArray();
bool skip = false;
char ch;
for (; i < len; i++)
{
ch = src[i];
switch (ch)
{
case '\u0020':
case '\u00A0':
case '\u1680':
case '\u2000':
case '\u2001':
case '\u2002':
case '\u2003':
case '\u2004':
case '\u2005':
case '\u2006':
case '\u2007':
case '\u2008':
case '\u2009':
case '\u200A':
case '\u202F':
case '\u205F':
case '\u3000':
case '\u2028':
case '\u2029':
case '\u0009':
case '\u000A':
case '\u000B':
case '\u000C':
case '\u000D':
case '\u0085':
if (skip) continue;
src[index++] = ch;
skip = true;
continue;
default:
skip = false;
src[index++] = ch;
continue;
}
}
return new string(src, 0, index);
}
}
Tests:
[TestFixture]
public class RemoveExtraWhitespacesTest
{
private const string _text = "foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo ";
private const string _expected = "foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo foo bar foobar moo ";
private const int _iterations = 10000;
[Test]
public void Regex()
{
var result = TimeAction("Regex", () => RemoveExtraWhitespaces.WithRegex(_text));
Assert.AreEqual(_expected, result);
}
[Test]
public void RegexCompiled()
{
var compiledRegex = new Regex(@"\s+", RegexOptions.Compiled);
var result = TimeAction("RegexCompiled", () => RemoveExtraWhitespaces.WithRegexCompiled(compiledRegex, _text));
Assert.AreEqual(_expected, result);
}
[Test]
public void NormalizeWhiteSpace()
{
var result = TimeAction("NormalizeWhiteSpace", () => RemoveExtraWhitespaces.NormalizeWhiteSpace(_text));
Assert.AreEqual(_expected, result);
}
[Test]
public void NormalizeWhiteSpaceForLoop()
{
var result = TimeAction("NormalizeWhiteSpaceForLoop", () => RemoveExtraWhitespaces.NormalizeWhiteSpaceForLoop(_text));
Assert.AreEqual(_expected, result);
}
public string TimeAction(string name, Func<string> func)
{
var timer = Stopwatch.StartNew();
string result = string.Empty; ;
for (int i = 0; i < _iterations; i++)
{
result = func();
}
timer.Stop();
Console.WriteLine(string.Format("{0}: {1} ms", name, timer.ElapsedMilliseconds));
return result;
}
}
Ich verwende die folgenden Methoden - sie behandeln alle Whitespace-Zeichen nicht nur Leerzeichen, trimmen beide - führenden und nachlaufenden Whitespaces, entfernen zusätzliche Whitespaces und alle Whitespaces werden durch Leerzeichen ersetzt char (also haben wir ein einheitliches Leerzeichen). Und diese Methoden sind schnell .
public static String CompactWhitespaces( String s )
{
StringBuilder sb = new StringBuilder( s );
CompactWhitespaces( sb );
return sb.ToString();
}
public static void CompactWhitespaces( StringBuilder sb )
{
if( sb.Length == 0 )
return;
// set [start] to first not-whitespace char or to sb.Length
int start = 0;
while( start < sb.Length )
{
if( Char.IsWhiteSpace( sb[ start ] ) )
start++;
else
break;
}
// if [sb] has only whitespaces, then return empty string
if( start == sb.Length )
{
sb.Length = 0;
return;
}
// set [end] to last not-whitespace char
int end = sb.Length - 1;
while( end >= 0 )
{
if( Char.IsWhiteSpace( sb[ end ] ) )
end--;
else
break;
}
// compact string
int dest = 0;
bool previousIsWhitespace = false;
for( int i = start; i <= end; i++ )
{
if( Char.IsWhiteSpace( sb[ i ] ) )
{
if( !previousIsWhitespace )
{
previousIsWhitespace = true;
sb[ dest ] = ' ';
dest++;
}
}
else
{
previousIsWhitespace = false;
sb[ dest ] = sb[ i ];
dest++;
}
}
sb.Length = dest;
}
string text = "foo bar";
text = Regex.Replace(text, @"\s+", " ");
// text = "foo bar"
Diese Lösung funktioniert mit Leerzeichen, Registerkarten und Zeilenumbrüchen. Wenn Sie nur Leerzeichen wünschen, ersetzen Sie '\ s' durch ''.
string q = " Hello how are you doing?";
string a = String.Join(" ", q.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
Ich brauchte eine davon für größere Saiten und stellte mir die Routine vor.
Alle aufeinanderfolgenden Leerzeichen (einschließlich Tabulatoren, Zeilenumbrüche) werden durch das ersetzt, was sich in normalizeTo
..__ befindet. Führende/nachgestellte Leerzeichen werden entfernt.
Es ist etwa 8-mal schneller als ein RegEx mit meinen 5k-> 5mil-Zeichenketten.
internal static string NormalizeWhiteSpace(string input, char normalizeTo = ' ')
{
if (string.IsNullOrEmpty(input))
return string.Empty;
int current = 0;
char[] output = new char[input.Length];
bool skipped = false;
foreach (char c in input.ToCharArray())
{
if (char.IsWhiteSpace(c))
{
if (!skipped)
{
if (current > 0)
output[current++] = normalizeTo;
skipped = true;
}
}
else
{
skipped = false;
output[current++] = c;
}
}
return new string(output, 0, skipped ? current - 1 : current);
}
string yourWord = "beep boop baap beep boop baap beep";
yourWord = yourWord .Replace(" ", " |").Replace("| ", "").Replace("|", "");
Ich habe versucht, StringBuilder zu verwenden:
Hier ist die beste Balance zwischen Leistung und Lesbarkeit, die ich gefunden habe (mit 100.000 Iterations-Timing-Läufen). Manchmal testet dies schneller als eine weniger lesbare Version, höchstens 5% langsamer. Auf meiner kleinen Testzeichenfolge benötigt Regex 4.24x so viel Zeit.
public static string RemoveExtraWhitespace(string str)
{
var sb = new StringBuilder();
var prevIsWhitespace = false;
foreach (var ch in str)
{
var isWhitespace = char.IsWhiteSpace(ch);
if (prevIsWhitespace && isWhitespace)
{
continue;
}
sb.Append(ch);
prevIsWhitespace = isWhitespace;
}
return sb.ToString();
}
Es ist nicht schnell, aber wenn Einfachheit hilft, funktioniert das:
while (text.Contains(" ")) text=text.Replace(" ", " ");
Dieser Code funktioniert gut. Ich habe die Leistung nicht gemessen.
string text = " hello - world, here we go !!! a bc ";
string.Join(" ", text.Split().Where(x => x != ""));
// Output
// "hello - world, here we go !!! a bc"
Einige Anforderungen sind in dieser Frage nicht klar, worüber man nachdenken sollte.
Dies ist eine sehr effiziente Version, die alle Leerzeichen durch ein einzelnes Leerzeichen ersetzt und alle führenden und nachfolgenden Leerzeichen vor der for-Schleife entfernt.
public static string WhiteSpaceToSingleSpaces(string input)
{
if (input.Length < 2)
return input;
StringBuilder sb = new StringBuilder();
input = input.Trim();
char lastChar = input[0];
bool lastCharWhiteSpace = false;
for (int i = 1; i < input.Length; i++)
{
bool whiteSpace = char.IsWhiteSpace(input[i]);
//Skip duplicate whitespace characters
if (whiteSpace && lastCharWhiteSpace)
continue;
//Replace all whitespace with a single space.
if (whiteSpace)
sb.Append(' ');
else
sb.Append(input[i]);
//Keep track of the last character's whitespace status
lastCharWhiteSpace = whiteSpace;
}
return sb.ToString();
}
Ich weiß nicht, ob es der schnellste Weg ist, aber ich benutze dies und das funktioniert für mich:
/// <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();
}
versuche dies:
System.Text.RegularExpressions.Regex.Replace(input, @"\s+", " ");
public string GetCorrectString(string IncorrectString)
{
string[] strarray = IncorrectString.Split(' ');
var sb = new StringBuilder();
foreach (var str in strarray)
{
if (str != string.Empty)
{
sb.Append(str).Append(' ');
}
}
return sb.ToString().Trim();
}
public static string RemoveExtraSpaces(string input)
{
input = input.Trim();
string output = "";
bool WasLastCharSpace = false;
for (int i = 0; i < input.Length; i++)
{
if (input[i] == ' ' && WasLastCharSpace)
continue;
WasLastCharSpace = input[i] == ' ';
output += input[i];
}
return output;
}
Für diejenigen, die nur kopieren und einfügen möchten:
private string RemoveExcessiveWhitespace(string value)
{
if (value == null) { return null; }
var builder = new StringBuilder();
var ignoreWhitespace = false;
foreach (var c in value)
{
if (!ignoreWhitespace || c != ' ')
{
builder.Append(c);
}
ignoreWhitespace = c == ' ';
}
return builder.ToString();
}
Ich weiß, dass dies sehr alt ist, aber der einfachste Weg, Leerzeichen zu verdichten (ersetzen Sie alle wiederkehrenden Leerzeichen durch ein einzelnes Leerzeichen) ist wie folgt:
public static string CompactWhitespace(string astring)
{
if (!string.IsNullOrEmpty(astring))
{
bool found = false;
StringBuilder buff = new StringBuilder();
foreach (char chr in astring.Trim())
{
if (char.IsWhiteSpace(chr))
{
if (found)
{
continue;
}
found = true;
buff.Append(' ');
}
else
{
if (found)
{
found = false;
}
buff.Append(chr);
}
}
return buff.ToString();
}
return string.Empty;
}
Ich bin mit C # nicht sehr vertraut, daher ist mein Code nicht elegant/effizient. Ich bin hierher gekommen, um eine Antwort zu finden, die zu meinem Anwendungsfall passt, aber ich konnte keine finden (oder ich konnte keine finden).
Für meinen Anwendungsfall musste ich alle White Spaces (WS: {space
, tab
, cr lf
}) mit den folgenden Bedingungen normalisieren:
tab
muss in einigen Fällen beibehalten werden (z. B. eine tabulatorgetrennte Datei und in diesem Fall müssen auch wiederholte Tabs beibehalten werden). In den meisten Fällen müssen sie jedoch in Leerzeichen umgewandelt werden.Hier also eine Beispieleingabe und eine erwartete Ausgabe (Haftungsausschluss: Mein Code ist nur für dieses Beispiel getestet).
Every night in my dreams I see you, I feel you
That's how I know you go on
Far across the distance and places between us
You have come to show you go on
umgewandelt werden in
Every night in my dreams I see you, I feel you
That's how I know you go on
Far across the distance and places between us
You have come to show you go on
Hier ist mein Code
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main(string text)
{
bool preserveTabs = false;
//[Step 1]: Clean up white spaces around the text
text = text.Trim();
//Console.Write("\nTrim\n======\n" + text);
//[Step 2]: Reduce repeated spaces to single space.
text = Regex.Replace(text, @" +", " ");
// Console.Write("\nNo repeated spaces\n======\n" + text);
//[Step 3]: Hande Tab spaces. Tabs needs to treated with care because
//in some files tabs have special meaning (for eg Tab seperated files)
if(preserveTabs)
{
text = Regex.Replace(text, @" *\t *", "\t");
}
else
{
text = Regex.Replace(text, @"[ \t]+", " ");
}
//Console.Write("\nTabs preserved\n======\n" + text);
//[Step 4]: Reduce repeated new lines (and other white spaces around them)
//into a single new line.
text = Regex.Replace(text, @"([\t ]*(\n)+[\t ]*)+", "\n");
Console.Write("\nClean New Lines\n======\n" + text);
}
}
Sehen Sie diesen Code hier in Aktion: https://dotnetfiddle.net/eupjIU
Fehlt mir hier etwas? Ich kam dazu:
// Input: "HELLO BEAUTIFUL WORLD!"
private string NormalizeWhitespace(string inputStr)
{
// First split the string on the spaces but exclude the spaces themselves
// Using the input string the length of the array will be 3. If the spaces
// were not filtered out they would be included in the array
var splitParts = inputStr.Split(' ').Where(x => x != "").ToArray();
// Now iterate over the parts in the array and add them to the return
// string. If the current part is not the last part, add a space after.
for (int i = 0; i < splitParts.Count(); i++)
{
retVal += splitParts[i];
if (i != splitParts.Count() - 1)
{
retVal += " ";
}
}
return retVal;
}
// Would return "HELLO BEAUTIFUL WORLD!"
Ich weiß, dass ich hier eine zweite Zeichenfolge erstellt, um sie zurückzugeben, sowie das splitParts-Array. Nur gedacht, das ist ziemlich geradlinig. Vielleicht berücksichtige ich einige der möglichen Szenarien nicht.
sie können indexOf verwenden, um zuerst den Anfang der Whitespace-Sequenzen zu ermitteln, und dann die Methode replace zu verwenden, um den Leerraum in "" zu ändern. Von dort aus können Sie den Index verwenden, den Sie mitgenommen haben, und einen Whitespace-Charakter an dieser Stelle platzieren.
Das ist lustig, aber auf meinem PC ist die unten beschriebene Methode genauso schnell wie der StringBulder-Ansatz von Sergey Povalyaev - (~ 282ms für 1000 Wiederholungen, 10k-Strings). Nicht sicher über die Speichernutzung.
string RemoveExtraWhiteSpace(string src, char[] wsChars){
return string.Join(" ",src.Split(wsChars, StringSplitOptions.RemoveEmptyEntries));
}
Offensichtlich funktioniert es gut mit beliebigen Zeichen - nicht nur mit Leerzeichen.
Das ist zwar nicht das, wonach das OP gefragt hat - aber wenn Sie wirklich bestimmte aufeinanderfolgende Zeichen in einer Zeichenfolge durch nur eine Instanz ersetzen müssen, können Sie diese relativ effiziente Methode verwenden:
string RemoveDuplicateChars(string src, char[] dupes){
var sd = (char[])dupes.Clone();
Array.Sort(sd);
var res = new StringBuilder(src.Length);
for(int i = 0; i<src.Length; i++){
if( i==0 || src[i]!=src[i-1] || Array.BinarySearch(sd,src[i])<0){
res.Append(src[i]);
}
}
return res.ToString();
}
Ich habe es gerade aufgemischt, habe es aber noch nicht getestet. Aber ich fand das elegant und vermeidet Regex:
/// <summary>
/// Removes extra white space.
/// </summary>
/// <param name="s">
/// The string
/// </param>
/// <returns>
/// The string, with only single white-space groupings.
/// </returns>
public static string RemoveExtraWhiteSpace(this string s)
{
if (s.Length == 0)
{
return string.Empty;
}
var stringBuilder = new StringBuilder();
var whiteSpaceCount = 0;
foreach (var character in s)
{
if (char.IsWhiteSpace(character))
{
whiteSpaceCount++;
}
else
{
whiteSpaceCount = 0;
}
if (whiteSpaceCount > 1)
{
continue;
}
stringBuilder.Append(character);
}
return stringBuilder.ToString();
}