web-dev-qa-db-ja.com

文字列に(文字列の)リストからの要素が含まれているかどうかを確認します

次のコードブロックの場合:

For I = 0 To listOfStrings.Count - 1
    If myString.Contains(lstOfStrings.Item(I)) Then
        Return True
    End If
Next
Return False

出力は次のとおりです。

ケース1:

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

ケース2:

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

リスト(listOfStrings)には複数のアイテム(最低20)が含まれている場合があり、数千のストリング(myStringなど)に対してチェックする必要があります。

このコードを書くためのより良い(より効率的な)方法はありますか?

136
user57175

LINQを使用し、C#を使用する(最近[VB] _をよく知らない):

bool b = listOfStrings.Any(s=>myString.Contains(s));

または(より短く、より効率的ですが、ほぼ明確ではありません):

bool b = listOfStrings.Any(myString.Contains);

同等性をテストしている場合、HashSetなどを調べる価値がありますが、フラグメントに分割して複雑さの順序を追加しない限り、これは部分一致には役立ちません。


更新:本当に "StartsWith"を意味する場合、リストを並べ替えて配列に入れることができます。次にArray.BinarySearchを使用して各アイテムを見つけます-ルックアップでチェックして、完全一致または部分一致かどうかを確認します。

309
Marc Gravell

あなたの文字列を構築するときは、このようにする必要があります

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));
6
Simi2525

以前の同様の質問から多くの提案がありました。「 同等の多数のリストに対して既存の文字列をテストする最良の方法 」。

正規表現で十分な場合があります。この式は、すべての候補部分文字列の連結であり、OR "|"演算子が間にあります。もちろん、式を作成するときはエスケープされていない文字に注意する必要があります。または、複雑さやサイズの制限のために式をコンパイルできない場合があります。

これを行う別の方法は、すべての候補部分文字列を表す trie data structure を構築することです(これは、正規表現マッチャーが実行していることをいくらか複製する可能性があります)。テスト文字列の各文字をステップ実行すると、トライのルートへの新しいポインターを作成し、既存のポインターを適切な子(存在する場合)に進めます。ポインターが葉に到達すると一致します。

5
Zach Scrivena

私はマークの答えが好きでしたが、CaSe InSenSiTiVeであるためには、Containsマッチングが必要でした。

これが解決策でした:

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))
3
WhoIsRich

パターンに基づく1つの改善点は、ContainsではなくStartsWithを使用するように変更することです。 StartsWithは、文字列が見つかったときにすべての文字位置で検索を再開する代わりに、最初の不一致が見つかるまで各文字列を反復処理するだけで済みます。

また、パターンに基づいて、myStringのパスの最初の部分を抽出し、次に比較を逆にすることができるように見えます-逆方向ではなく、文字列のリストでmyStringの開始パスを探します。

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;

return listOfStrings.Contains( startPath );

EDITContainsContainsKeyに変更でき、ルックアップがO(1) O(N)の代わりに。パスが正確に一致することを確認する必要があります。これは@Marc Gravellのような一般的な解決策ではありませんが、例に合わせて調整されていることに注意してください。

C#の例でごめんなさい。 VBに変換するのに十分なコーヒーがありません。

2
tvanfosson

速度をテストしましたか?

つまり、データのサンプルセットを作成してプロファイルを作成しましたか?思ったほど悪くないかもしれません。

これは、別のスレッドに生成され、スピードの錯覚を与えることができるものかもしれません!

1
Fortyrunner

それがより効率的かどうかはわかりませんが、 Lambda Expressions での使用について考えることができます。

1
Mark Carpenter

Containsメソッドの欠点は、文字列を比較するときにしばしば重要となる比較タイプを指定できないことです。常に文化と大文字と小文字が区別されます。だから、WhoIsRichの答えは価値があると思います。もっと単純な代替案を示したいだけです。

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
0
Al Kepp
myList.Any(myString.Contains);
0
WIRN

古い質問。しかし、VB.NETが元の要件だったため。受け入れられた答えと同じ値を使用する:

listOfStrings.Any(Function(s) myString.Contains(s))
0
Luis Lavieri

速度が重要な場合は、パターンのセットについて Aho-Corasickアルゴリズム を探してください。

それは トライ 失敗リンクです。つまり、複雑さはO(n + m + k)です。ここで、nは入力テキストの長さ、mはパターンの累積長、kは一致します。最初の一致が見つかった後に終了するようにアルゴリズムを修正する必要があります。

0
Torsten Marek