[c#] 여러 공백을 하나의 공백으로 바꾸는 방법
다음과 같은 문자열이 있다고 가정 해 보겠습니다.
"Hello how are you doing?"
여러 공간을 하나의 공간으로 바꾸는 기능을 원합니다.
그래서 나는 얻을 것이다 :
"Hello how are you doing?"
정규식을 사용하거나 전화를 걸 수 있다는 것을 알고 있습니다.
string s = "Hello how are you doing?".replace(" "," ");
그러나 모든 순차 공백이 단 하나로 대체되도록 여러 번 호출해야합니다.
이미 내장 된 방법이 있습니까?
답변
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
답변
이 질문은 다른 포스터가 만들어 낸 것만 큼 간단하지 않습니다 (그리고 제가 원래 믿었던 것처럼)-질문이 필요한만큼 정확하지 않기 때문입니다.
“공백”과 “공백”에는 차이가 있습니다. 당신이 경우 에만 공간을 의미, 당신은의 정규식을 사용한다 " {2,}"
. 공백 을 의미 하는 경우 다른 문제입니다. 모든 공백을 공백으로 변환 해야합니까 ? 시작과 끝에서 공간은 어떻게 되나요?
아래 벤치 마크의 경우 공백에만 관심이 있고 시작과 끝에서도 단일 공백에 대해 아무것도하고 싶지 않다고 가정했습니다.
정확성은 거의 항상 성능보다 중요합니다. 분할 / 결합 솔루션이 선행 / 후행 공백 (단지 단일 공백도 포함)을 제거한다는 사실은 지정된 요구 사항 (물론 불완전 할 수 있음)에 한하면 올바르지 않습니다.
벤치 마크는 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
여기서 첫 번째 숫자는 반복 횟수이고 두 번째 숫자는 소요 시간이며 세 번째 숫자는 1.0이 가장 좋은 척도입니다.
이는 적어도 일부 경우 (이 경우 포함)에서 정규식 이 분할 / 조인 솔루션을 능가 할 수 있으며 때로는 매우 큰 차이를 보일 수 있음을 보여줍니다.
그러나 “모든 공백”요구 사항으로 변경하면 분할 / 결합 이 승리하는 것처럼 보입니다. 자주 그렇듯이 악마는 세부 사항에 있습니다 …
답변
정규 표현이 가장 쉬운 방법입니다. 정규식을 올바른 방식으로 작성하면 여러 번 호출 할 필요가 없습니다.
다음과 같이 변경하십시오.
string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " ");
답변
기존 답변은 괜찮지 만 작동 하지 않는 한 가지 접근 방식을 지적하고 싶습니다 .
public static string DontUseThisToCollapseSpaces(string text)
{
while (text.IndexOf(" ") != -1)
{
text = text.Replace(" ", " ");
}
return text;
}
이것은 영원히 반복 될 수 있습니다. 왜 그럴까요? (몇 년 전에 뉴스 그룹 질문으로 질문을 받았을 때만이 문제를 발견했습니다 … 누군가 실제로 문제로 부딪 혔습니다.)
답변
이미 지적했듯이 이것은 정규 표현식으로 쉽게 수행됩니다. 선행 / 후행 공백을 제거하기 위해 여기에 .trim ()을 추가하고 싶을 수도 있음을 추가하겠습니다.
답변
여기 내가 함께 일하는 솔루션이 있습니다. 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();
답변
빠른 추가 공백 제거기 … 이것은 가장 빠른 방법이며 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 Felipe Machado on CodeProject 2015 및 Sunsetquest에 의해 다중 공간 제거를 위해 수정되었습니다.
시간 : 3.75 틱
에 의해 InPlaceCharArray 펠리페 마차도 2015 약간 다중 공간 제거 Sunsetquest에 의해 수정.
시간 6.50 틱 (탭도 지원)
Jon Skeet의 SplitAndJoinOnSpace .
시간 : 13.25 틱
Fubo
시간별 StringBuilder : 13.5 틱 (탭도 지원)
Jon Skeet의 컴파일로 정규식 .
시간 : 17 틱
StringBuilder by David S 2013
Time : 30.5 틱
Brandon
Time의 컴파일되지 않은 정규식 : 63.25 Ticks
user214147에
의한 StringBuilder 시간 : 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 회 실행, 짧은 문자열 만 테스트 됨