web-dev-qa-db-ja.com

文字列からすべての空白を削除するための効率的な方法は?

私はREST AP​​Iを呼び出していて、XML応答を受け取っています。それはワークスペース名のリストを返します、そして私は簡単なIsExistingWorkspace()メソッドを書いています。すべてのワークスペースは空白のない連続した文字で構成されているので、特定のワークスペースがリストに含まれているかどうかを調べる最も簡単な方法は、すべての空白を削除することです要求):

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

私はそれが大文字と小文字を区別することを知っています、そして私はそれに頼っています。文字列内のすべての空白を効率的に削除する方法が必要です。私はRegExとLINQがそれを実行できることを知っていますが、私は他のアイデアにも寛容です。私は主にスピードが心配です。

308
Corey Ogburn

正規表現を使用したくないと言ったとしても、これは私が知っている最も速い方法です。

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

私は正規表現なしで別の方法を持っています、そしてそれはかなり良いパフォーマンスをしているようです。これは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回の試行で最初のオプション(正規表現なし)は1秒もかからず(私のマシンでは700ミリ秒)、2番目のオプションは3.5秒かかります。

160

C#で文字列のreplaceメソッドを試してください。

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

私の解決策は分割と結合を使用することであり、それは驚くほど速く、実際にここで最も速い回答の中で最も速いです。

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));

空白文字を含む単純な文字列の10,000ループのタイミング

  • 分割/結合= 60ミリ秒
  • linq chararray = 94ミリ秒
  • 正規表現= 437ミリ秒

意味を与えるためにmethodでそれをラップすることによってこれを改善し、そしてまた我々がそれにいる間それを拡張メソッドにする...

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
55
kernowcode

Henks answer 私は彼の答えといくつかの追加された、より最適化されたメソッドでいくつかのテストメソッドを作成しました。入力文字列のサイズによって結果が異なることがわかりました。したがって、私は2つの結果セットでテストしました。最速の方法では、リンクされたソースはもっと速い方法を持っています。しかし、それは安全ではないとして特徴付けられるので、私はこれを省きました。

長い入力文字列の結果:

  1. InPlaceCharArray:2021ミリ秒( Sunsetquestの回答 ) - ( オリジナルのソース
  2. 分割して結合する文字列:4277ms( Kernowcode's answer
  3. 文字列リーダー:6082ミリ秒
  4. ネイティブchar.IsWhitespaceを使用してLINQ:7357ミリ秒
  5. LINQ:7746 ms( ヘンクの答え
  6. ForLoop:32320ミリ秒
  7. RegexCompiled:37157ミリ秒
  8. 正規表現:42940ミリ秒

短い入力文字列の結果:

  1. InPlaceCharArray:108ミリ秒( Sunsetquestの回答 ) - ( オリジナルのソース
  2. 文字列を分割して結合する:294ミリ秒( Kernowcode's answer
  3. 文字列リーダー:327ミリ秒
  4. ForLoop:343ミリ秒
  5. ネイティブchar.IsWhitespaceを使用してLINQ:624ミリ秒
  6. LINQ:645ms( ヘンクの答え
  7. RegexCompiled:1671ミリ秒
  8. 正規表現:2599ミリ秒

コード

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のNice one Linerをテストしました。

33
Stian Standahl

それは非常にいいように見えるのでちょうど代替手段:) - 注: ヘンクスの答え はこれらの中で最も速いです。

input.ToCharArray()
 .Where(c => !Char.IsWhiteSpace(c))
 .Select(c => c.ToString())
 .Aggregate((a, b) => a + b);

"This is a simple Test"で1,000,000ループをテストする

この方法は1.74秒です
正規表現= 2.58秒
new String(ヘンクス)= 0.82

23
BlueChippy

私は これに関する素晴らしい記事 Felipe MachadoによるCodeProjectで見つけました( Richard Robertson の助けを借りて)

彼は10の異なる方法をテストしました。これは最速unsafeバージョンです...

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

そして最速のsafeバージョン...

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の関数が2番目に速い関数よりも約300%高速であることを示すNice 独立したベンチマーク もあります。

17
Sunsetquest

優れたパフォーマンスが必要な場合は、この場合は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);
    }
13
JHM

正規表現はやり過ぎです。文字列に拡張子を付けるだけです(Henkさん)。これは些細なことであり、フレームワークの一部であったはずです。とにかく、これが私の実装です:

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

これは、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();
}
4
Brandon Moretz

文字列の空白を空白に置き換える必要がありますが、重複する空白は置き換えないでください。たとえば、次のように変換する必要がありました。

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

あなたの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");
2
dtb

これはまた別の変種です:

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

他のほとんどのソリューションと同様に、徹底的なベンチマークテストは行っていませんが、これは私の目的には十分に適しています。

1
Fred

使用できます:

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

私は異なる結果が真実であることを発見しました。すべての空白を単一の空白に置き換えようとしていますが、正規表現が非常に遅くなっていました。

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分後に正規表現が中断されました。
0
hvanbrug