[C#] 문자열에서 모든 공백을 제거하는 효율적인 방법?

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의 답변바탕 으로 그의 답변과 몇 가지 테스트 방법을 추가하고보다 최적화 된 방법을 만들었습니다. 입력 문자열의 크기에 따라 결과가 다릅니다. 따라서 두 가지 결과 집합으로 테스트했습니다. 가장 빠른 방법으로 연결된 소스는 더 빠른 방법을 사용합니다. 그러나 안전하지 않은 것으로 특징 지어 졌으므로 이것을 제외했습니다.

긴 입력 문자열 결과 :

  1. InPlaceCharArray : 2021ms ( Sunsetquest의 답변 )-( 원본 소스 )
  2. 문자열 분할 후 조인 : 4277ms ( Kernowcode의 답변 )
  3. 문자열 판독기 : 6082ms
  4. 네이티브 char.IsWhitespace를 사용하는 LINQ : 7357ms
  5. LINQ : 7746ms ( Henk ‘s answer )
  6. ForLoop : 32320ms
  7. 정규식 : 37157ms
  8. 정규식 : 42940ms

짧은 입력 문자열 결과 :

  1. InPlaceCharArray : 108ms ( Sunsetquest의 답변 )-( 원본 소스 )
  2. 문자열 분할 후 조인 : 294ms ( Kernowcode의 답변 )
  3. 문자열 판독기 : 327ms
  4. ForLoop : 343ms
  5. 네이티브 char.IsWhitespace를 사용하는 LINQ : 624ms
  6. LINQ : 645ms (Hank ‘s answer )
  7. 정규식 컴파일 : 1671ms
  8. 정규식 : 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 answer 가 가장 빠릅니다.

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 % 더 빠릅니다.