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


358

REST API를 호출하고 XML 응답을 받고 있습니다. 작업 공간 이름 목록을 반환하며 빠른 IsExistingWorkspace()방법을 작성 중 입니다. 모든 작업 공간은 공백이없는 연속 문자로 구성되므로 특정 작업 공간이 목록에 있는지 확인하는 가장 쉬운 방법은 모든 공백 (줄 바꿈 포함)을 제거 하고이 작업을 수행하는 것입니다 (XML은 웹에서받은 문자열입니다) 의뢰):

XML.Contains("<name>" + workspaceName + "</name>");

대소 문자를 구분한다는 것을 알고 있으며 그것에 의존하고 있습니다. 문자열의 모든 공백을 효율적으로 제거하는 방법이 필요합니다. RegEx와 LINQ가 할 수 있다는 것을 알고 있지만 다른 아이디어에 열려 있습니다. 나는 주로 속도에 관심이 있습니다.


6
정규식으로 XML을 구문 분석하는 것은 정규식으로 HTML을 구문 분석하는 것 만큼이나 나쁩니다 .
dtb

3
@henk holterman; 아래 답변을 참조하십시오 .regexp는 모든 경우에 가장 빠른 것처럼 보이지 않습니다.
Henk J Meulekamp

정규식은 전혀 빠르지 않은 것 같습니다. 문자열에서 공백을 제거하는 여러 가지 방법의 결과를 요약했습니다. - 요약은 아래 답변에 stackoverflow.com/a/37347881/582061
스티 Standahl

답변:


616

정규식을 사용하고 싶지 않다고 말했지만 이것이 가장 빠른 방법입니다.

Regex.Replace(XML, @"\s+", "")

1
정규 표현식을 사용할 수 있습니다. 가장 빠른 방법인지 확실하지 않습니다.
Corey Ogburn

1
나는 그것이 확실하다. 최소한 뒤에서 모든 문자를 확인해야하며 이것은 선형 검색을 수행하는 것입니다.
slandau 2016 년

19
그렇지 Regex.Replace(XML, @"\s+", "")않습니까?
Jan-Peter Vos 2016 년

61
이 작업을 두 번 이상 수행하려는 경우 Regex 인스턴스를 만들어 저장하십시오. 이렇게하면 매번 구성하는 오버 헤드가 절약되어 생각보다 비쌉니다. private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
hypehuman

10
RegEx를 처음 사용하고이 표현의 의미에 대한 설명을 찾으려는 경우 \s"공백 토큰과 일치"및 +"하나 이상의 진행 토큰과 일치"를 의미합니다. 또한 RegExr 은 실험하고 싶다면 RegEx 표현식 작성을 연습 할 수있는 훌륭한 웹 사이트입니다.
jrh

181

정규 표현식이없는 다른 방법이 있으며 성능이 매우 좋습니다. 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 초가 걸립니다.


40
.ToCharArray()필요가 없습니다; .Where()문자열에서 직접 사용할 수 있습니다 .
ProgramFOX

10
여기에 주목하십시오. 작은 문자열에서 정규식이 느려집니다! 당신이 소수의 반복과 함께 미국 세법 (~ 백만 단어?)에 대한 볼륨의 디지털 버전을 가지고 있다고 말하면, Regex는 지금까지 왕입니다! 더 빠르지는 않지만 어떤 상황에서 사용해야합니까? 여기서 방정식의 절반 만 증명했습니다. 테스트의 후반부를 증명할 때까지 -1은 응답이 언제 사용해야하는지에 대한 더 많은 통찰력을 제공합니다.
Piotr Kula

17
@ppumkin 그는 공백의 단일 패스 제거를 요청했습니다. 다른 처리를 여러 번 반복하지 않습니다. 이 단일 패스 공백 제거를 벤치마킹 텍스트 처리에 대한 확장 된 게시물로 만들지 않겠습니다.
Henk J Meulekamp 2016 년

1
이번에는 정규 표현식을 사용하지 않는 것이 좋지만 왜 그런지 말하지 않았습니다.
Piotr Kula

2
@ProgramFOX, 다른 질문 (쉽게 찾을 수 없음) 적어도 일부 쿼리 에서 문자열 ToCharArray.Where()직접 사용 하는 것보다 사용이 빠릅니다 . 이것은 IEnumerable<>각 반복 단계에서 오버 헤드 와 관련이 ToCharArray있으며 매우 효율적 이며 (블록 복사) 컴파일러는 배열에 대한 반복을 최적화합니다. 이 차이가 존재하는 이유는 아무도 설명 할 수 없었지만 제거하기 전에 측정하십시오 ToCharArray().
Abel

87

C #에서 문자열의 바꾸기 방법을 시도하십시오.

XML.Replace(" ", string.Empty);

28
탭이나 개행을 제거하지 않습니다. 여러 번 제거하면 문자열을 여러 번 통과합니다.
Corey Ogburn

11
Slandau와 Henk의 답변처럼 모든 공백을 제거하지 않은 공감대.
Matt Sach

@MattSach 왜 모든 공백을 제거하지 않습니까?
Zapnologica

4
@Zapnologica 공백 문자 만 바꿉니다. OP는 개행 문자 (공백 문자가 아닌 경우에도 "공백"문자)를 교체하도록 요청했습니다.
Matt Sach

75

내 솔루션은 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));
}

3
나는이 솔루션을 정말 좋아합니다 .LINQ 전부터 비슷한 솔루션을 사용하고 있습니다. 나는 실제로 LINQs 성능에 깊은 인상을 받았으며 정규 표현식에 다소 놀랐습니다. 아마도 코드가 정규 표현식보다 최적이 아닐 수도 있습니다 (예를 들어 정규 표현식 객체를 캐시해야 함). 그러나 문제의 핵심은 데이터의 "품질"이 매우 중요하다는 것입니다. 긴 문자열을 사용하면 정규 표현식이 다른 옵션보다 성능이 뛰어납니다. 그것은 수행하는 재미있는 벤치 마크가 될 것입니다 ... :-)
Loudenvier

1
default (string []) == 모든 공백 문자 목록은 어떻게됩니까? 나는 그것이 작동하는 것을 보았지만 어떻게 이해하지 못합니까?
Jake Drew

5
당신은 2와 오버로드 사이의 모호한 의미 @kernowcode string[]char[]? 원하는 것을 지정하면 string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));됩니다. 예 : . 실제로이 default경우에도 호출이 수행 되므로 호출해야 null합니다. 컴파일러가 선택할 과부하를 결정하는 데 도움이됩니다. 따라서 내 의견은 "Split에는 유효한 배열이 필요하고 null 은하 지 않습니다 ..."라는 문장의 진술이 거짓이기 때문에 내 의견입니다. 제이크 드류 (Jake Drew)가 어떻게 작동했는지 물었을 때부터 언급 할 가치가 있다고 생각했습니다. 답을 +1
Frank J

6
멋진 아이디어 ...하지만 나는 다음과 같이 할 것입니다 :string.Concat("H \ne llo Wor ld".Split())
michaelkrisper

3
michaelkrisper 솔루션은 매우 읽기 쉽습니다. 나는 테스트를했고 같은 문자열의 10,000 회 반복에 대해 'split / concat'(180 밀리 초)보다 'split / join'(162 밀리 초)이 더 잘 수행되었습니다.
kernowcode

45

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에서 멋진 라이너 하나를 테스트했습니다.


24

아주 좋은 것처럼 보이기 때문에 대안이 될 것입니다 :)-참고 : 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


1
이것이 왜 하향 조정 되었습니까? 완벽하게 수용 가능하고 요구 사항을 충족하며 RegEx 옵션보다 빠르게 작동하며 읽을 수 있습니까?
BlueChippy

4
훨씬 짧게 쓸 수 있기 때문에 : new string (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit

7
사실 일 수도 있지만 대답은 여전히 ​​유효하고 읽기 쉽고 정규 표현식보다 빠르며 원하는 결과를 생성합니다. 다른 답변들 중 다수는이 답변 이후에 있습니다. 따라서 downvote는 의미가 없습니다.
BlueChippy

2
"0.82"를위한 단위가 있습니까? 아니면 상대 척도입니까 (82 %)? 더 명확하게 답변을 편집 할 수 있습니까?
피터 Mortensen

20

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


이것을 C ++로 변환하려고 시도했지만 조금 붙어 있습니다. 내 포트가 실패한 이유가 있습니까? stackoverflow.com/questions/42135922/…
Jon Cage

2
저항 할 수 없어 언급 한 기사의 의견 섹션을보십시오. 당신은 저를 "바스켓 케이스 소프트웨어"로 찾을 것입니다. 그와 함께 잠시 동안 함께 일했습니다. 이 문제가 다시 발생했을 때 나는 이것에 대해 완전히 잊었다. 좋은 추억 주셔서 감사합니다. :)
Richard Robertson

1
추가 WS 만 제거하려면 어떻게해야합니까? 이 stackoverflow.com/questions/17770202/… mod는 어떻습니까?

컨테이너 속도가 더 빠를수록 빠릅니다 .-) 여기에서 컨테이너 성능이 더 좋습니다 (앱 4:15 ~ 3:55 => 8.5 % 감소), 왼쪽 문자열 3:30 => 21.4 % 감소하고 프로 필러가 소비 된 약 50 %를 보여줍니다 이 방법). 따라서 실제 라이브 문자열에서는 여기에서 사용되는 (느린) 배열 변환에 비해 약 40 % 더 빠릅니다.

15

최상의 성능이 필요한 경우이 경우 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);
    }

나는 당신의 벤치마킹에 대한 자세한 내용을 알고 싶어합니다. 회의적이지는 않지만 Linq와 관련된 오버 헤드가 궁금합니다. 얼마나 나빴어?
Mark Meuer

나는 모든 테스트를 다시 실행하지는 않았지만 이것을 많이 기억할 수 있습니다. Linq와 관련된 모든 것이 그것 없이는 다른 것보다 훨씬 느 렸습니다. Linq가 사용 된 경우 문자열 / char 함수와 생성자의 모든 영리한 사용법은 아무런 차이가 없었습니다.
JHM

11

정규식은 과잉입니다. 문자열에 확장자를 사용하십시오 (Hanks 덕분에). 이것은 사소한 것이며 프레임 워크의 일부 여야합니다. 어쨌든, 여기 내 구현이 있습니다 :

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}

이것은 기본적으로 불필요한 답변입니다 (정규식은 과잉이지만 주어진 것보다 빠른 해결책이며 이미 허용됩니까?)
W1ll1amvl

Linq 확장 방법을 문자열에 어떻게 사용할 수 있습니까? 나보다 다른 사람을 잃었 사용하여 알아낼 수 없습니다System.Linq
GGirard

좋아, 이것이 PCL에서 사용할 수없는 것처럼 보입니다. IEnumerable <char>은 Microsoft String 구현 에서 조건부입니다 ... 그리고 이것을 지원하지 않는 Profile259를 사용하고 있습니다 :)
GGirard

4

다음은 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();
}

3

문자열의 공백을 공백으로 바꾸어야하지만 공백은 복제하지 않아야합니다. 예를 들어 다음과 같은 것을 변환해야했습니다.

"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();
}

2

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");

특정 <name> 태그가 적절한 값을 가지고 있는지 확인하면 완료됩니다. 문서를 파싱하는 데 약간의 오버 헤드가 있습니까?
Corey Ogburn

4
물론 약간의 오버 헤드가 있습니다. 그러나 그것은 정확하다는 이점이 있습니다. 예를 들어 정규 표현식을 기반으로 한 솔루션은 훨씬 더 어려워집니다. LINQ to XML 솔루션이 너무 느리다고 판단되면 언제든지 더 빠른 솔루션으로 교체 할 수 있습니다. 그러나 올바른 구현이 너무 느리다는 것을 알기 전에 가장 효율적인 구현을 위해 헌팅을 피해야합니다.
dtb

이것은 고용주의 백엔드 서버에서 실행될 것입니다. 내가 찾는 것은 경량입니다. 나는 "그냥 작동하는"것을 원하지 않지만 최적의 것을 원합니다.
Corey Ogburn

4
LINQ to XML은 .NET에서 XML을 올바르게 사용하는 가장 간단한 방법 중 하나입니다.
dtb

1

여기 또 다른 변형이 있습니다 :

public static string RemoveAllWhitespace(string aString)
{
  return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}

대부분의 다른 솔루션과 마찬가지로 철저한 벤치 마크 테스트를 수행하지는 않았지만 이것은 내 목적에 충분합니다.


1

우리는 사용할 수 있습니다 :

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }

이것은 위의 Henk의 답변과 거의 동일합니다. 유일한 차이점은 확인하는 것입니다 null.
Corey Ogburn 2016 년

예, null인지 확인하십시오 importente
Tarik BENARAB

1
아마도 이것은 그의 대답에 대한 주석이었을 것입니다. 그래도 기뻐요. null 메서드에서 확장 메서드를 호출 할 수 있다는 것을 몰랐습니다.
Corey Ogburn 2016 년

0

다른 결과가 사실이라는 것을 알았습니다. 모든 공백을 단일 공백으로 바꾸려고하는데 정규 표현식이 매우 느립니다.

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 분 후에 정규 표현식이 중단되었습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.