余分な空白を1つの空白に置き換える最も速い方法は何ですか?
例えば。
from
foo bar
to
foo bar
最速の方法?文字列を反復処理し、StringBuilder
に文字ごとに2番目のコピーを作成し、スペースのグループごとに1つのスペースのみをコピーします。
タイプしやすいReplace
バリアントは、余分な文字列のバケット負荷を作成します(または正規表現DFAの構築に時間を浪費します)。
比較結果で編集:
http://ideone.com/NV6Ez を使用し、n = 50で(プロセスを強制終了するのに時間がかかりすぎたため、ideoneで削減する必要がありました)、次のようになります。
正規表現:7771ms。
文字列ビルダー:894ms。
確かに予想どおりです。Regex
は、これほど単純なものに対して恐ろしく非効率的です。
正規表現を使用できます:
static readonly Regex trimmer = new Regex(@"\s\s+");
s = trimmer.Replace(s, " ");
パフォーマンスを向上させるには、RegexOptions.Compiled
を渡します。
少し遅れましたが、余分な空白を削除するための最速の方法を得るために、いくつかのベンチマークを実行しました。より速い答えがあれば、私はそれらを追加したいと思います。
結果:
コード:
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);
}
}
テスト:
[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;
}
}
私は以下のメソッドを使用します-それらはall whitespace charsスペースだけでなく、leadingとtrailing whitespaces、remove extraの両方をトリムします=空白、およびすべての空白はスペースに置き換えられます charです(したがって、均一なスペース区切りがあります)。そして、これらのメソッドはfastです。
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 q = " Hello how are you doing?";
string a = String.Join(" ", q.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries));
string text = "foo bar";
text = Regex.Replace(text, @"\s+", " ");
// text = "foo bar"
このソリューションは、スペース、タブ、改行で機能します。スペースだけが必要な場合は、「\ s」を「」に置き換えます。
大きな文字列にはこれらのいずれかが必要で、以下のルーチンを思いつきました。
連続する空白(タブ、改行を含む)は、normalizeTo
にあるもので置き換えられます。先頭/末尾の空白は削除されます。
5k-> 5milの文字列を持つRegExの約8倍高速です。
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("|", "");
私はStringBuilderを使って次のことを試みました。
これが、私が見つけたパフォーマンスと可読性の最適なバランスです(100,000回の反復タイミング実行を使用)。時々、これは読みにくいバージョンよりも速く、最大で5%遅くなります。私の小さなテスト文字列では、正規表現は4.24倍の時間がかかります。
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();
}
それは高速ではありませんが、シンプルさが役立つ場合、これは動作します:
while (text.Contains(" ")) text=text.Replace(" ", " ");
このコードはうまく機能します。パフォーマンスを測定していません。
string text = " hello - world, here we go !!! a bc ";
string.Join(" ", text.Split().Where(x => x != ""));
// Output
// "hello - world, here we go !!! a bc"
この質問では、いくつかの要件が明確ではなく、いくつかの考慮に値します。
これは非常に効率的なバージョンで、すべての空白を単一のスペースに置き換え、forループの前にある先頭および末尾の空白を削除します。
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();
}
これを試して:
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;
}
コピーして貼り付けて続行する場合:
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();
}
私はこれが本当に古いことを知っていますが、空白を圧縮する最も簡単な方法(繰り返される空白文字を単一の「スペース」文字に置き換える)は次のとおりです。
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;
}
私はC#にあまり詳しくないので、私のコードは洗練された/最も効率的なコードではありません。ユースケースに合った答えを見つけるためにここに来ましたが、見つけられませんでした(または見つけられませんでした)。
ユースケースでは、すべての空白(WS:{space
、tab
、cr lf
})次の条件で:
tab
を保持する必要があります(たとえば、タブで区切られたファイル、およびその場合、繰り返しタブも保存する必要があります)。ただし、ほとんどの場合、スペースに変換する必要があります。サンプル入力と期待される出力は次のとおりです(免責事項:このコードはこの例でのみテストされています)
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
に変換される
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
これが私のコードです
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);
}
}
ここで実際にこのコードを参照してください: https://dotnetfiddle.net/eupjI
ここに何かが足りませんか?私はこれを思いつきました:
// 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!"
SplitParts配列を作成するのと同様に、ここに2番目の文字列を作成して返すことを知っています。これはかなり簡単だと思っただけです。おそらく、いくつかの潜在的なシナリオを考慮していないのかもしれません。
それが最速の方法であるかどうかはわかりませんが、私はこれを使用し、これは私のために働いています:
/// <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();
}
indexOfを使用して最初に空白シーケンスの開始位置を取得し、次にreplaceメソッドを使用して空白を「」に変更できます。そこから、取得したインデックスを使用して、その場所に1つの空白文字を配置できます。
これはおもしろいですが、私のPCでは、以下の方法はSergey PovalyaevのStringBulderアプローチと同じくらい高速です(1000反復で約282ms、10000 src文字列)。ただし、メモリ使用量についてはわかりません。
string RemoveExtraWhiteSpace(string src, char[] wsChars){
return string.Join(" ",src.Split(wsChars, StringSplitOptions.RemoveEmptyEntries));
}
明らかにスペースだけでなく、どんな文字でも問題なく動作します。
これはOPが求めたものではありませんが、本当に必要なのは、文字列内の特定の連続した文字を1つのインスタンスのみで置き換えることである場合、この比較的効率的な方法を使用できます:
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();
}
私はこれをただホイップしましたが、まだテストしていません。しかし、私はこれがエレガントで、正規表現を避けていると感じました:
/// <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();
}