web-dev-qa-db-ja.com

文字列をきれいにしますか?それを行うより良い方法はありますか?

このメソッドを使用して文字列をクリーンアップしています

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のような

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

22
patel.milanb

次のテストについて考えてみましょう。

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メソッド(L.Bが作成)を使用します。ただし、少し異なることに注意してください。

41
sloth

それが純粋に速度と効率である場合は、次のようなことをお勧めします。

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回目)。これにより、メモリ使用量が削減され、速度が向上します(特に長い文字列の場合)。

ただし、L.B。ただし、これを使用してHTML出力用にバインドされたテキストを適切にエンコードする場合は、自分で行う代わりにHttpUtility.HtmlEncodeを使用する必要があります。

5
Steven Doggart

regex [?&^$#@!()+-,:;<>’\'-_*]を使用して、空の文字列に置き換えます

3
burning_LEGION

パフォーマンス面でRegexまたはLINQを使用すると改善されるかどうかはわかりません。
役立つ可能性があるものは、毎回string.Replaceを使用する代わりに、StringBuilderを使用して新しい文字列を作成することです。

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();
    }
}
2
Paolo Tedesco

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

1
Stuart.Sklinar

おそらく、最初に「理由」を説明し、次に「何」を説明すると役立つでしょう。パフォーマンスが遅くなるのは、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文字を処理する場合は正しくありませんが、これらの文字を含めるように簡単に調整できます。

-ステファン。

1
atlaste

これはもっと速いです!
使用する:

string dirty=@"tfgtf$@$%gttg%$% 664%$";
string clean = dirty.Clean();


    public static string Clean(this String name)
    {
        var namearray = new Char[name.Length];

        var newIndex = 0;
        for (var index = 0; index < namearray.Length; index++)
        {
            var letter = (Int32)name[index];

            if (!((letter > 96 && letter < 123) || (letter > 64 && letter < 91) || (letter > 47 && letter < 58)))
                continue;

            namearray[newIndex] = (Char)letter;
            ++newIndex;
        }

        return new String(namearray).TrimEnd();
    }
1
gd73

これを酸でテストすることに時間を費やすことはできませんが、このラインは実際にスラッシュを望み通りにきれいにしませんでした。

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

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

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

私は現在のプロジェクトでこれを使用しており、正常に動作します。これは文を受け取り、英数字以外のすべての文字を削除し、最初の文字がすべて大文字で、それ以外のすべてが小文字の文を返します。多分私はそれをSentenceNormalizerと呼ぶべきです。命名は難しい:)

    internal static string StringSanitizer(string whateverString)
{
    whateverString = whateverString.Trim().ToLower();
    Regex cleaner = new Regex("(?:[^a-zA-Z0-9 ])", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
    var listOfWords = (cleaner.Replace(whateverString, string.Empty).Split(' ', StringSplitOptions.RemoveEmptyEntries)).ToList();
    string cleanString = string.Empty;
    foreach (string Word in listOfWords)
    {
        cleanString += $"{Word.First().ToString().ToUpper() + Word.Substring(1)} ";
    }
    return cleanString;
}
0
Manuel.B