it-roy-ru.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).

Есть ли лучший (более эффективный) способ написания этого кода?

127
user57175

С LINQ и использованием C # (я не знаю VB в наши дни):

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

или (короче и эффективнее, но, возможно, менее понятно):

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

Если бы вы проверяли равенство, стоило бы посмотреть на HashSet и т.д., Но это не поможет с частичными совпадениями, если вы не разбили его на фрагменты и не добавили порядок сложности.


обновление: если вы действительно имеете в виду «StartsWith», то вы можете отсортировать список и поместить его в массив; затем используйте Array.BinarySearch, чтобы найти каждый элемент - проверьте поиск, чтобы убедиться, что это полное или частичное совпадение.

283
Marc Gravell

когда вы строите свои строки, это должно быть так

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

Было несколько предложений от ранее подобного вопроса " Лучший способ проверить существующую строку по большому списку сопоставимых ".

Regex может быть достаточно для вашего требования. Выражение будет объединением всех подстрок-кандидатов с оператором OR «|» между ними. Конечно, вам придется следить за неэкранированными символами при построении выражения или за невозможностью скомпилировать его из-за сложности или ограничений по размеру.

Другой способ сделать это состоит в том, чтобы создать три структуры данных для представления всех подстрок-кандидатов (это может несколько дублировать то, что делает сопоставитель регулярных выражений). По мере продвижения по каждому символу в тестовой строке вы будете создавать новый указатель на корень дерева и продвигать существующие указатели к соответствующему дочернему элементу (если есть). Вы получаете совпадение, когда любой указатель достигает листа.

5
Zach Scrivena

Мне понравился ответ Марка, но мне нужно, чтобы соответствие Contains имело значение CASE INSENSiTiVe.

Это было решение:

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

Основываясь на ваших шаблонах, одним из улучшений будет переход на использование StartsWith вместо Contains. StartsWith нужно только перебирать каждую строку до тех пор, пока не найдет первое несоответствие, вместо того, чтобы начинать поиск с каждой позиции символа, когда он ее найдет.

Кроме того, основываясь на ваших шаблонах, похоже, что вы сможете извлечь первую часть пути для myString, а затем отменить сравнение - искать начальный путь myString в списке строк, а не наоборот.

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

return listOfStrings.Contains( startPath );

EDIT: Это было бы еще быстрее, если использовать идею HashSet, о которой упоминает @Marc Gravell, поскольку вы можете изменить Contains на ContainsKey и поиск будет O(1) вместо O (N). Вы должны убедиться, что пути совпадают точно. Обратите внимание, что это не общее решение, как у @Marc Gravell, но оно адаптировано к вашим примерам.

Извините за пример C #. У меня не было достаточно кофе, чтобы перевести на VB.

2
tvanfosson

Вы тестировали скорость?

вы создали образец набора данных и профилировали его? Это может быть не так плохо, как вы думаете.

Это также может быть что-то, что вы можете создать в отдельном потоке и создать иллюзию скорости!

1
Fortyrunner

Я не уверен, что это более эффективно, но вы можете подумать об использовании в лямбда-выражений .

1
Mark Carpenter

Недостаток метода Contains заключается в том, что он не позволяет указать тип сравнения, который часто важен при сравнении строк. Он всегда чувствителен к культуре и регистру. Поэтому я думаю, что ответ WhoIsRich является ценным, я просто хочу показать более простую альтернативу:

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

Если скорость критична, вы можете искать алгоритм Aho-Corasick для наборов паттернов. 

Это trie со ссылками на ошибки, то есть сложность O (n + m + k), где n - длина входного текста, m - совокупная длина шаблонов и k - количество совпадений. Вам просто нужно изменить алгоритм, чтобы завершить работу после того, как найдено первое совпадение.

0
Torsten Marek