27

このメソッドを使用して文字列をきれいにしています

public static string CleanString(string dirtyString)
{
    string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
    string result = dirtyString;

    foreach (char c in removeChars)
    {
        result = result.Replace(c.ToString(), string.Empty);
    }

    return result;
}

この方法は正常に機能します..しかし、この方法にはパフォーマンスの不具合があります。文字列を渡すたびに、すべての文字がループに入ります。大きな文字列があると、オブジェクトを返すのに時間がかかりすぎます。

同じことを行うための他のより良い方法はありますか? LINQ や JQUERY / Javascript のように

任意の提案をいただければ幸いです。

4

9 に答える 9

48

OK、次のテストを検討してください。

public class CleanString
{
    //by MSDN http://msdn.microsoft.com/en-us/library/844skk0h(v=vs.71).aspx
    public static string UseRegex(string strIn)
    {
        // Replace invalid characters with empty strings.
        return Regex.Replace(strIn, @"[^\w\.@-]", "");
    }

    // by Paolo Tedesco
    public static String UseStringBuilder(string strIn)
    {
        const string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
        // specify capacity of StringBuilder to avoid resizing
        StringBuilder sb = new StringBuilder(strIn.Length);
        foreach (char x in strIn.Where(c => !removeChars.Contains(c)))
        {
            sb.Append(x);
        }
        return sb.ToString();
    }

    // by Paolo Tedesco, but using a HashSet
    public static String UseStringBuilderWithHashSet(string strIn)
    {
        var hashSet = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");
        // specify capacity of StringBuilder to avoid resizing
        StringBuilder sb = new StringBuilder(strIn.Length);
        foreach (char x in strIn.Where(c => !hashSet.Contains(c)))
        {
            sb.Append(x);
        }
        return sb.ToString();
    }

    // by SteveDog
    public static string UseStringBuilderWithHashSet2(string dirtyString)
    {
        HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");
        StringBuilder result = new StringBuilder(dirtyString.Length);
        foreach (char c in dirtyString)
            if (removeChars.Contains(c))
                result.Append(c);
        return result.ToString();
    }

    // original by patel.milanb
    public static string UseReplace(string dirtyString)
    {
        string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
        string result = dirtyString;

        foreach (char c in removeChars)
        {
            result = result.Replace(c.ToString(), string.Empty);
        }

        return result;
    }

    // by L.B
    public static string UseWhere(string dirtyString)
    {
        return new String(dirtyString.Where(Char.IsLetterOrDigit).ToArray());
    }
}

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        var dirtyString = "sdfdf.dsf8908()=(=(sadfJJLef@ssyd€sdöf////fj()=/§(§&/(\"&sdfdf.dsf8908()=(=(sadfJJLef@ssyd€sdöf////fj()=/§(§&/(\"&sdfdf.dsf8908()=(=(sadfJJLef@ssyd€sdöf";
        var sw = new Stopwatch();

        var iterations = 50000;

        sw.Start();
        for (var i = 0; i < iterations; i++)
            CleanString.<SomeMethod>(dirtyString);
        sw.Stop();
        Debug.WriteLine("CleanString.<SomeMethod>: " + sw.ElapsedMilliseconds.ToString());
        sw.Reset();

        ....
        <repeat>
        ....       
    }
}

出力

CleanString.UseReplace: 791
CleanString.UseStringBuilder: 2805
CleanString.UseStringBuilderWithHashSet: 521
CleanString.UseStringBuilderWithHashSet2: 331
CleanString.UseRegex: 1700
CleanString.UseWhere: 233

結論

おそらく、どの方法を使用してもかまいません。

UseWhere高速 ( : 233ms) のメソッドと最も遅い ( UseStringBuilder: 2805ms) のメソッドの時間差は、 50000 回 (!) 回連続して呼び出した場合、2572ms です。メソッドをそれほど頻繁に実行しない場合は、おそらく気にする必要はないはずです。

ただし、そうする場合は、UseWhereメソッド (LB によって作成された) を使用してください。しかし、それはわずかに異なることに注意してください。

于 2012-07-09T13:29:48.343 に答える
7

純粋に速度と効率を追求する場合は、次のようにすることをお勧めします。

public static string CleanString(string dirtyString)
{
    HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");
    StringBuilder result = new StringBuilder(dirtyString.Length);
    foreach (char c in dirtyString)
        if (!removeChars.Contains(c)) // prevent dirty chars
            result.Append(c);
    return result.ToString();
}

RegEx は確かに洗練されたソリューションですが、余分なオーバーヘッドが追加されます。文字列ビルダの開始長を指定することにより、メモリを 1 回割り当てるだけで済みます (ToString最後に 2 回目)。これにより、メモリ使用量が削減され、特に長い文字列で速度が向上します。

ただし、LB が言ったように、HTML 出力用にバインドされたテキストを適切にエンコードするためにこれを使用している場合は、HttpUtility.HtmlEncode自分で行うのではなく、使用する必要があります。

于 2012-07-09T13:29:47.260 に答える
4

[?&^$#@!()+-,:;<>’\'-_*]空の文字列に置き換えるために正規表現を使用する

于 2012-07-09T13:16:17.950 に答える
2

RegexまたはLINQを使用するとパフォーマンスが向上するかどうかはわかりません。毎回使用するのではなく、を使用
して新しい文字列を作成すると便利です。StringBuilderstring.Replace

using System.Linq;
using System.Text;

static class Program {
    static void Main(string[] args) {
        const string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
        string result = "x&y(z)";
        // specify capacity of StringBuilder to avoid resizing
        StringBuilder sb = new StringBuilder(result.Length);
        foreach (char x in result.Where(c => !removeChars.Contains(c))) {
            sb.Append(x);
        }
        result = sb.ToString();
    }
}
于 2012-07-09T13:30:01.787 に答える
1

これを試してみてください: http://msdn.microsoft.com/en-us/library/xwewhkd1.aspx

于 2012-07-09T13:17:19.057 に答える
1

おそらく、最初に「なぜ」を説明し、次に「何を」説明すると役立つでしょう。パフォーマンスが低下する理由は、C# が各置換の文字列をコピーして置換するためです。私の経験から、.NET で Regex を使用することが常に良いとは限りません。ただし、ほとんどのシナリオ (これを含むと思います) では、おそらく問題なく動作するでしょう。

本当にパフォーマンスが必要な場合は、通常は運任せにせず、必要なことをコンパイラーに正確に伝えます。つまり、上限文字数の文字列を作成し、そこに必要なすべての文字をコピーします。ハッシュセットをスイッチ/ケースまたは配列に置き換えることも可能です。その場合、ジャンプ テーブルまたは配列ルックアップになる可能性があります。これはさらに高速です。

「実用的な」最良の、しかし迅速な解決策は次のとおりです。

char[] data = new char[dirtyString.Length];
int ptr = 0;
HashSet<char> hs = new HashSet<char>() { /* all your excluded chars go here */ };
foreach (char c in dirtyString)
    if (!hs.Contains(c))
        data[ptr++] = c;
return new string(data, 0, ptr);

ところで: この解決策は、高サロゲート Unicode 文字を処理したい場合には正しくありませんが、これらの文字を含めるように簡単に適応させることができます。

-ステファン。

于 2012-07-09T13:51:30.397 に答える
0

これを酸テストするのに時間を費やすことはできませんが、この行は実際には必要に応じてスラッシュをきれいにしませんでした。

HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");

スラッシュを個別に追加し、バックスラッシュをエスケープする必要がありました

HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’'-_*");
removeChars.Add('/');
removeChars.Add('\\');
于 2015-01-21T20:05:41.793 に答える