次のような文字列があるとします。
"Hello how are you doing?"
複数のスペースを1つのスペースに変換する機能が欲しいです。
だから私は得るだろう:
"Hello how are you doing?"
正規表現を使用するか、電話をかけることができることを知っています
string s = "Hello how are you doing?".replace(" "," ");
しかし、すべての連続する空白が1つだけに置き換えられるように、複数回呼び出す必要があります。
このための組み込みメソッドは既にありますか?
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
この質問は、他のポスターが作成したほど単純ではありません(そして、私が当初信じていたように)-質問は必要なほど正確ではないためです。
「スペース」と「ホワイトスペース」には違いがあります。スペースを意味するonlyの場合、" {2,}"
の正規表現を使用する必要があります。空白anyを意味する場合、それは別の問題です。 allホワイトスペースをスペースに変換する必要がありますか?開始時と終了時にスペースはどうなりますか?
以下のベンチマークでは、スペースのみに関心があり、開始時と終了時でも単一のスペースに対しては何もしたくないと想定しています。
ほとんどの場合、パフォーマンスよりも正確さが重要です。分割/結合ソリューションが先頭/末尾の空白(単一のスペースでも)を削除するという事実は、指定された要件(もちろん不完全な場合もあります)に関しては正しくありません。
ベンチマークは 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);
}
}
いくつかのテストの実行:
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
ここで、最初の数値は反復回数、2番目は所要時間、3番目は1.0が最良のスケーリングされたスコアです。
これは、少なくともいくつかの場合(これを含む)で、正規表現canが分割/結合ソリューションよりも優れている場合があることを示しています。
ただし、「すべて空白」の要件に変更すると、分割/結合doesが勝つように見えます。よくあることですが、悪魔は詳細に...
既存の答えは問題ありませんが、does n'tが機能する1つのアプローチを指摘したいと思います。
public static string DontUseThisToCollapseSpaces(string text)
{
while (text.IndexOf(" ") != -1)
{
text = text.Replace(" ", " ");
}
return text;
}
これは永久にループする可能性があります。誰が理由を推測したいですか? (数年前にニュースグループの質問として尋ねられたとき、私はこれに遭遇しました...誰かが実際に問題としてそれに遭遇しました。)
正規表現が最も簡単な方法です。正規表現を正しい方法で記述する場合、複数の呼び出しは必要ありません。
これに変更します:
string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " ");
ここに私が働くソリューションがあります。 RegExおよび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();
}
だからあなたはできる:
string cleanedString = dirtyString.TrimWhiteSpace();
既に指摘したように、これは正規表現によって簡単に行われます。そこに.trim()を追加して、先頭または末尾の空白を削除することもできます。
何か違うものを思いついたように見えるので、使用しているものを共有しています。私はしばらくこれを使用してきましたが、十分に高速です。私はそれが他のものに対してどのように積み重なるのか分かりません。区切りファイルライターで使用し、一度に1フィールドずつ大きなデータテーブルを実行します。
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();
}
高速の余分な空白文字の削除...これは最速のもので、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);
}
ベンチマーク...
InPlaceCharArraySpaceOnly by CodeProjectのFelipe Machado 2015で、マルチスペース削除のためにSunsetquestによって変更されました。 時間:3.75ティック
Felipe Machado 2015によるInPlaceCharArrayと、Sunsetquestによるマルチスペース削除のためのわずかな変更。 時間6.50ティック(タブもサポート)
SplitAndJoinOnSpace by Jon Skeet 。 時間:13.25ティック
StringBuilder by fubo時間:13.5ティック(タブもサポート)
Jon Skeet によるコンパイルを伴う正規表現。 時間:17ティック
StringBuilder by David S 2013時間:30.5ティック
ブランドンでコンパイルしない場合の正規表現時間:63.25ティック
StringBuilder by ser214147時間:77.125ティック
非コンパイル時の正規表現 Tim Hoolihan時間:147.25ティック
ベンチマークコード...
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;
}
}
ベンチマークノート:リリースモード、デバッガーなし、i7プロセッサー、4回の平均実行、短い文字列のみテスト
VB.NET
Linha.Split(" ").ToList().Where(Function(x) x <> " ").ToArray
C#
Linha.Split(" ").ToList().Where(x => x != " ").ToArray();
LINQ = Dのパワーをお楽しみください
Jon Skeetが投稿したテストプログラムを使用して、手書きのループを高速で実行できるかどうかを確認しようとしました。
NormalizeWithSplitAndJoinを毎回打つことができますが、入力1000、5でのみNormalizeWithRegexを打つことができます。
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();
}
ジッタが生成するマシンコードを見たことはありませんが、問題はStringBuilder.Append()の呼び出しにかかる時間であり、より良いことをするには安全でないコードの使用が必要になると予想しています。
つまり、Regex.Replace()は非常に速く、打ちにくいです!
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");
最小のソリューション:
var regExp =/\ s +/g、newString = oldString.replace(regExp、 '');
これを試すことができます:
/// <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();
}