3

入力文字列からストップワードの配列を削除したいのですが、次の手順があります

string[] arrToCheck = new string[] { "try ", "yourself", "before " };

string input = "Did you try this yourself before asking";
foreach (string word in arrToCheck )
{
input = input.Replace(word, "");
}

特に(450)ストップワードがあり、入力文字列が長い場合は、このタスクを実行するのに最適な方法ですか?異なる形態で現れるストップワードを削除したいので、replaceメソッドを使用することを好みます。たとえば、ストップワードが「do」の場合は、「do」を削除します(doing、doesなど)。より良く、最速の処理のための提案はありますか?前もって感謝します。

4

6 に答える 6

4

提案してもStringBuilderいいですか?

http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx

string[] arrToCheck = new string[] { "try ", "yourself", "before " };

StringBuilder input = new StringBuilder("Did you try this yourself before asking");
foreach (string word in arrToCheck )
{
    input.Replace(word, "");
}

独自のデータ構造内ですべての処理を実行し、何百もの新しい文字列を割り当てないため、メモリ効率がはるかに高いことがわかると思います。

于 2012-05-04T11:41:05.487 に答える
3

これにはいくつかの側面があります

時期尚早の最適化
与えられた方法は機能し、理解/維持が容易です。パフォーマンスの問題を引き起こしていますか?そうでない場合は、心配しないでください。問題が発生した場合は、それを確認してください。

期待される結果
この例では、出力をどのようにしますか?

"Did you this asking"

また

"Did you  this   asking"

「try」と「before」の最後にスペースを追加しましたが、「yourself」は追加していません。なんで?打ち間違え?

string.Replace()では大文字と小文字が区別されます。ケーシングが気になる場合は、コードを変更する必要があります。

パーシャルの操作は面倒です。
言葉は時制が異なります。「do」が「doing」の単語から削除された例ですが、「take」と「takeing」はどうですか?入力を変更するため、ストップワードの順序が重要になります。変更前に入力に含まれていなかった単語が、変更後に入力に「表示される」可能性があります(可能性はわかりませんが可能です)。毎回戻って再確認しますか?

本当にパーシャルを削除する必要がありますか?

最適化
現在の方法は、入力文字列をn回処理します。ここで、nは編集される単語の数であり、置換が発生するたびに新しい文字列を作成します。これ遅いです。

StringBuilder(上記のakatakritos)を使用すると、速度が上がるので、最初にこれを試してみます。再テストして、これで十分な速度になるかどうかを確認します。

Linqを使用できます

編集
デモのために''で分割するだけです。句読点も考慮して、句読点をどうするかを決める必要があります。
編集終了

[TestMethod]
public void RedactTextLinqNoPartials() {

    var arrToCheck = new string[] { "try", "yourself", "before" };
    var input = "Did you try this yourself before asking";

    var output = string.Join(" ",input.Split(' ').Where(wrd => !arrToCheck.Contains(wrd)));

    Assert.AreEqual("Did you this asking", output);

}

すべての単語全体(およびスペース。単語が削除された場所を確認することはできません)を削除しますが、ベンチマークがなければ、より高速であるとは言えません。

linqを使用したパーシャルの処理は面倒になりますが、パスが1つだけの場合は機能します(「検出された」単語のチェックは不要)

[TestMethod]
public void RedactTextLinqPartials() {

    var arrToCheck = new string[] { "try", "yourself", "before", "ask" };
    var input = "Did you try this yourself before asking";

    var output = string.Join(" ", input.Split(' ').Select(wrd => {
        var found = arrToCheck.FirstOrDefault(chk => wrd.IndexOf(chk) != -1);
            return found != null
                   ? wrd.Replace(found,"")
                   : wrd;
    }).Where(wrd => wrd != ""));


    Assert.AreEqual("Did you this ing", output);

}

これを見るだけで、string.Replace()よりも遅いと言えますが、いくつかの数値がないとわかりません。それは間違いなくもっと複雑です。

結論String.Replace
()アプローチ(文字列ビルダーを使用し、大文字と小文字を区別しないように変更)は、優れたファーストカットソリューションのように見えます。より複雑なことを試す前に、可能性のあるパフォーマンス条件下でベンチマークを行います。

hth、
アラン。

于 2012-05-04T12:56:15.210 に答える
3

どうぞ:

var words_to_remove = new HashSet<string> { "try", "yourself", "before" };
string input = "Did you try this yourself before asking";

string output = string.Join(
    " ",
    input
        .Split(new[] { ' ', '\t', '\n', '\r' /* etc... */ })
        .Where(word => !words_to_remove.Contains(word))
);

Console.WriteLine(output);

これは印刷します:

Did you this asking

HashSet非常に高速なルックアップを提供するため、450個の要素はwords_to_removeまったく問題ありません。また、入力文字列をトラバースするのは1回だけです(例のように単語ごとに1回削除するのではなく)。

ただし、入力文字列が非常に長い場合は、分割結果を一度にメモリに保持しないことで、これをよりメモリ効率の高いものにする方法があります(高速ではないにしても)。

「do」だけでなく「doing」、「does」などを削除するには、これらすべてのバリアントをに含める必要がありますwords_to_remove。一般的な方法でプレフィックスを削除したい場合は、削除する単語のトライ(または入力文字列のサフィックスツリー)を使用して(比較的)効率的に削除できますが、「do」「しました」など、削除する必要があるもののプレフィックス?または、「犬」など、削除してはいけないもののプレフィックスである場合はどうでしょうか。

ところで、大文字と小文字を区別せずに単語を削除するには、 StringComparer.CurrentCultureIgnoreCaseなどの適切な大文字と小文字を区別しない比較子をHashSetコンストラクターに渡すだけです。

- - 編集 - -

別の方法は次のとおりです。

var words_to_remove = new[] { " ", "try", "yourself", "before" }; // Note the space!
string input = "Did you try this yourself before asking";

string output = string.Join(
    " ",
    input.Split(words_to_remove, StringSplitOptions.RemoveEmptyEntries)
);

(内部でハッシュテーブルを使用しない限り)遅くなるはずstring.Splitですが、きれいで整頓されています;)

于 2012-05-04T20:06:17.853 に答える
1

文から文字列のリストを削除し、結果をまとめて戻す簡単な方法として、次の操作を実行できます。

var input = "Did you try this yourself before asking"; 
var arrToCheck = new [] { "try ", "yourself", "before " };
var result = input.Split(arrToCheck, 
                         arrToCheck.Count(), 
                         StringSplitOptions.None)
                  .Aggregate((first, second) => first + second);

これにより、元の文字列が単語の区切り文字で区切られ、分割された配列の結果セットを使用して1つの最終的な文字列が作成されます。

結果は、"Did you this before asking"

于 2012-05-04T11:42:35.160 に答える
0

コードを短くし、LINQを使用します

string[] arrToCheck = new string[] { "try ", "yourself", "before " };   
var test = new StringBuilder("Did you try this yourself before asking"); 

arrToCheck.ForEach(x=> test = test.Replace(x, "")); 

Console.Writeln(test.ToString());
于 2012-05-04T11:43:44.663 に答える
0
String.Join(" ",input.
          Split(' ').Where(w=>stop.Where(sW=>sW==w).
                   FirstOrDefault()==null).ToArray());
于 2012-05-04T11:47:36.623 に答える