문자열에서 모든 공백을 제거하는 효율적인 방법?
REST API를 호출하고 XML 응답을 받고 있습니다. 작업 공간 이름 목록을 반환하며 빠른 IsExistingWorkspace()
방법을 작성 중 입니다. 모든 작업 공간은 공백이없는 연속 문자로 구성되므로 특정 작업 공간이 목록에 있는지 확인하는 가장 쉬운 방법은 모든 공백 (줄 바꿈 포함)을 제거 하고이 작업을 수행하는 것입니다 (XML은 웹에서받은 문자열입니다) 의뢰):
XML.Contains("<name>" + workspaceName + "</name>");
대소 문자를 구분한다는 것을 알고 있으며 그것에 의존하고 있습니다. 문자열의 모든 공백을 효율적으로 제거하는 방법이 필요합니다. RegEx와 LINQ가 할 수 있다는 것을 알고 있지만 다른 아이디어에 열려 있습니다. 나는 주로 속도에 관심이 있습니다.
정규식을 사용하고 싶지 않다고 말했지만 이것이 가장 빠른 방법입니다.
Regex.Replace(XML, @"\s+", "")
정규 표현식이없는 다른 방법이 있으며 성능이 매우 좋습니다. Brandon Moretz의 답변은 계속됩니다.
public static string RemoveWhitespace(this string input)
{
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
간단한 단위 테스트에서 테스트했습니다.
[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace1(string input, string expected)
{
string s = null;
for (int i = 0; i < 1000000; i++)
{
s = input.RemoveWhitespace();
}
Assert.AreEqual(expected, s);
}
[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace2(string input, string expected)
{
string s = null;
for (int i = 0; i < 1000000; i++)
{
s = Regex.Replace(input, @"\s+", "");
}
Assert.AreEqual(expected, s);
}
1,000,000 회 시도의 경우 첫 번째 옵션 (regexp 제외)이 1 초 미만 (내 컴퓨터에서 700ms) 이내에 실행되고 두 번째 옵션은 3.5 초가 걸립니다.
C #에서 문자열의 바꾸기 방법을 시도하십시오.
XML.Replace(" ", string.Empty);
내 솔루션은 Split and Join을 사용하는 것이며 놀랍게도 빠르며 실제로 가장 빠른 답변 중 가장 빠릅니다.
str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
공백을 포함하여 새 줄과 탭이있는 간단한 문자열의 10,000 루프 타이밍
- 분할 / 결합 = 60 밀리 초
- linq chararray = 94 밀리 초
- 정규식 = 437 밀리 초
의미를 부여하기 위해 메소드로 감싸서 이것을 개선하고, 우리가있는 동안 확장 메소드로 만드십시오 ...
public static string RemoveWhitespace(this string str) {
return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
Henks의 답변 을 바탕 으로 그의 답변과 몇 가지 테스트 방법을 만들고 최적화 된 추가 방법을 만들었습니다. 입력 문자열의 크기에 따라 결과가 다릅니다. 따라서 두 가지 결과 집합으로 테스트했습니다. 가장 빠른 방법으로 링크 된 소스는 더 빠른 방법을 사용합니다. 그러나 안전하지 않은 것으로 특징 지어 졌으므로 이것을 제외했습니다.
긴 입력 문자열 결과 :
- InPlaceCharArray : 2021ms ( Sunsetquest의 답변 )-( 원본 소스 )
- 문자열 분할 후 조인 : 4277ms ( Kernowcode의 답변 )
- 문자열 판독기 : 6082ms
- 네이티브 char.IsWhitespace를 사용하는 LINQ : 7357ms
- LINQ : 7746ms ( Henk 's answer )
- ForLoop : 32320ms
- 정규식 : 37157ms
- 정규식 : 42940ms
짧은 입력 문자열 결과 :
- InPlaceCharArray : 108ms ( Sunsetquest의 답변 )-( 원본 소스 )
- 문자열 분할 후 결합 : 294ms ( Kernowcode의 답변 )
- 문자열 판독기 : 327ms
- ForLoop : 343ms
- 네이티브 char.IsWhitespace를 사용하는 LINQ : 624ms
- LINQ : 645ms (Hank 's answer )
- 정규식 컴파일 : 1671ms
- 정규식 : 2599ms
코드 :
public class RemoveWhitespace
{
public static string RemoveStringReader(string input)
{
var s = new StringBuilder(input.Length); // (input.Length);
using (var reader = new StringReader(input))
{
int i = 0;
char c;
for (; i < input.Length; i++)
{
c = (char)reader.Read();
if (!char.IsWhiteSpace(c))
{
s.Append(c);
}
}
}
return s.ToString();
}
public static string RemoveLinqNativeCharIsWhitespace(string input)
{
return new string(input.ToCharArray()
.Where(c => !char.IsWhiteSpace(c))
.ToArray());
}
public static string RemoveLinq(string input)
{
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
public static string RemoveRegex(string input)
{
return Regex.Replace(input, @"\s+", "");
}
private static Regex compiled = new Regex(@"\s+", RegexOptions.Compiled);
public static string RemoveRegexCompiled(string input)
{
return compiled.Replace(input, "");
}
public static string RemoveForLoop(string input)
{
for (int i = input.Length - 1; i >= 0; i--)
{
if (char.IsWhiteSpace(input[i]))
{
input = input.Remove(i, 1);
}
}
return input;
}
public static string StringSplitThenJoin(this string str)
{
return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
public static string RemoveInPlaceCharArray(string input)
{
var len = input.Length;
var src = input.ToCharArray();
int dstIdx = 0;
for (int i = 0; i < len; i++)
{
var 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':
continue;
default:
src[dstIdx++] = ch;
break;
}
}
return new string(src, 0, dstIdx);
}
}
테스트 :
[TestFixture]
public class Test
{
// Short input
//private const string input = "123 123 \t 1adc \n 222";
//private const string expected = "1231231adc222";
// Long input
private const string input = "123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222";
private const string expected = "1231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc222";
private const int iterations = 1000000;
[Test]
public void RemoveInPlaceCharArray()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveInPlaceCharArray(input);
}
stopwatch.Stop();
Console.WriteLine("InPlaceCharArray: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveStringReader()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveStringReader(input);
}
stopwatch.Stop();
Console.WriteLine("String reader: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveLinqNativeCharIsWhitespace()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveLinqNativeCharIsWhitespace(input);
}
stopwatch.Stop();
Console.WriteLine("LINQ using native char.IsWhitespace: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveLinq()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveLinq(input);
}
stopwatch.Stop();
Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveRegex()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveRegex(input);
}
stopwatch.Stop();
Console.WriteLine("Regex: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveRegexCompiled()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveRegexCompiled(input);
}
stopwatch.Stop();
Console.WriteLine("RegexCompiled: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveForLoop()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveForLoop(input);
}
stopwatch.Stop();
Console.WriteLine("ForLoop: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[TestMethod]
public void StringSplitThenJoin()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.StringSplitThenJoin(input);
}
stopwatch.Stop();
Console.WriteLine("StringSplitThenJoin: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
}
편집 : Kernowcode에서 멋진 라이너 하나를 테스트했습니다.
꽤 좋은 :)을 보이기 때문에 그냥 대안 - 참고 : Henks의 대답 이 가장 빠른 것입니다.
input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.Select(c => c.ToString())
.Aggregate((a, b) => a + b);
1,000,000 루프 테스트 "This is a simple Test"
이 방법 = 1.74 초
정규식 = 2.58 초
new String
(He) = 0.82
Felipe Machado의 CodeProject 에 대한 훌륭한 글을 찾았 습니다 ( Richard Robertson의 도움으로 )
그는 10 가지 방법을 테스트했습니다. 이것은 가장 빠른 안전하지 않은 버전입니다 ...
public static unsafe string TrimAllWithStringInplace(string str) {
fixed (char* pfixed = str) {
char* dst = pfixed;
for (char* p = pfixed; *p != 0; p++)
switch (*p) {
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':
continue;
default:
*dst++ = *p;
break;
}
return new string(pfixed, 0, (int)(dst - pfixed));
}
그리고 가장 안전한 버전은 ...
public static string TrimAllWithInplaceCharArray(string str) {
var len = str.Length;
var src = str.ToCharArray();
int dstIdx = 0;
for (int i = 0; i < len; i++) {
var 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':
continue;
default:
src[dstIdx++] = ch;
break;
}
}
return new string(src, 0, dstIdx);
}
Stian Standahl의 Stack Overflow에 대한 훌륭한 독립 벤치 마크 도 있으며 Felipe의 기능이 다음으로 빠른 기능보다 약 300 % 더 빠릅니다.
최상의 성능이 필요한 경우이 경우 LINQ 및 정규식을 피해야합니다. 성능 벤치마킹을했는데 문자열의 시작과 끝에서 공백을 제거하려면 string.Trim ()이 궁극적 인 함수 인 것 같습니다.
문자열에서 모든 공백을 제거해야하는 경우 다음 방법은 여기에 게시 된 것 중에서 가장 빠르게 작동합니다.
public static string RemoveWhitespace(this string input)
{
int j = 0, inputlen = input.Length;
char[] newarr = new char[inputlen];
for (int i = 0; i < inputlen; ++i)
{
char tmp = input[i];
if (!char.IsWhiteSpace(tmp))
{
newarr[j] = tmp;
++j;
}
}
return new String(newarr, 0, j);
}
정규식은 과잉입니다. 문자열에 확장자를 사용하십시오 (Hanks 덕분에). 이것은 사소한 것이며 프레임 워크의 일부 여야합니다. 어쨌든, 여기 내 구현이 있습니다 :
public static partial class Extension
{
public static string RemoveWhiteSpace(this string self)
{
return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
}
}
다음은 RegEx 솔루션에 대한 간단한 선형 대안입니다. 어느 쪽이 더 빠른지 잘 모르겠습니다. 벤치마킹해야합니다.
static string RemoveWhitespace(string input)
{
StringBuilder output = new StringBuilder(input.Length);
for (int index = 0; index < input.Length; index++)
{
if (!Char.IsWhiteSpace(input, index))
{
output.Append(input[index]);
}
}
return output.ToString();
}
문자열의 공백을 공백으로 바꿔야하지만 공백은 복제하지 않아야했습니다. 예를 들어 다음과 같은 것을 변환해야했습니다.
"a b c\r\n d\t\t\t e"
에
"a b c d e"
나는 다음 방법을 사용했다
private static string RemoveWhiteSpace(string value)
{
if (value == null) { return null; }
var sb = new StringBuilder();
var lastCharWs = false;
foreach (var c in value)
{
if (char.IsWhiteSpace(c))
{
if (lastCharWs) { continue; }
sb.Append(' ');
lastCharWs = true;
}
else
{
sb.Append(c);
lastCharWs = false;
}
}
return sb.ToString();
}
XML 응답이 다음과 같다고 가정합니다.
var xml = @"<names>
<name>
foo
</name>
<name>
bar
</name>
</names>";
XML을 처리하는 가장 좋은 방법은 LINQ to XML 과 같은 XML 파서를 사용하는 것입니다 .
var doc = XDocument.Parse(xml);
var containsFoo = doc.Root
.Elements("name")
.Any(e => ((string)e).Trim() == "foo");
여기 또 다른 변형이 있습니다 :
public static string RemoveAllWhitespace(string aString)
{
return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}
대부분의 다른 솔루션과 마찬가지로 철저한 벤치 마크 테스트를 수행하지는 않았지만 이것은 내 목적에 충분합니다.
우리는 사용할 수 있습니다 :
public static string RemoveWhitespace(this string input)
{
if (input == null)
return null;
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
다른 결과가 사실이라는 것을 알았습니다. 모든 공백을 단일 공백으로 바꾸려고하는데 정규 표현식이 매우 느립니다.
return( Regex::Replace( text, L"\s+", L" " ) );
나를 위해 가장 최적으로 일한 것은 (C ++ cli에서) 다음과 같습니다.
String^ ReduceWhitespace( String^ text )
{
String^ newText;
bool inWhitespace = false;
Int32 posStart = 0;
Int32 pos = 0;
for( pos = 0; pos < text->Length; ++pos )
{
wchar_t cc = text[pos];
if( Char::IsWhiteSpace( cc ) )
{
if( !inWhitespace )
{
if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
inWhitespace = true;
newText += L' ';
}
posStart = pos + 1;
}
else
{
if( inWhitespace )
{
inWhitespace = false;
posStart = pos;
}
}
}
if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
return( newText );
}
각 문자를 개별적으로 교체하여 위의 루틴을 먼저 시도했지만 공백이 아닌 섹션에 대해 하위 문자열을 수행하도록 전환해야했습니다. 1,200,000 문자열에 적용 할 때 :
- 위의 루틴은 25 초 안에 완료됩니다.
- 위의 루틴 + 95 초 안에 문자 교체
- 15 분 후에 정규 표현식이 중단되었습니다.
참고 URL : https://stackoverflow.com/questions/6219454/efficient-way-to-remove-all-whitespace-from-string
'Programing' 카테고리의 다른 글
.NET에서 'for'또는 'foreach'중 어느 루프가 더 빠르게 실행됩니까? (0) | 2020.03.08 |
---|---|
Cocoapods 경고-프로젝트에 이미 사용자 정의 구성 세트가 있으므로 CocoaPods가 프로젝트의 기본 구성을 설정하지 않았습니다. (0) | 2020.03.08 |
실용적인 비 이미지 기반 보안 문자 접근 방식? (0) | 2020.03.08 |
git에서 전체 태그와 함께 모든 태그를 나열하는 방법은 무엇입니까? (0) | 2020.03.08 |
LD_PRELOAD 트릭은 무엇입니까? (0) | 2020.03.08 |