Проверьте, содержит ли строка элемент из списка (строк) - PullRequest
132 голосов
/ 01 февраля 2009

Для следующего блока кода:

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).

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

Ответы [ 10 ]

299 голосов
/ 01 февраля 2009

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

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

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

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

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


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

6 голосов
/ 19 декабря 2014

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

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));
5 голосов
/ 01 февраля 2009

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

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

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

3 голосов
/ 01 мая 2014

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

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

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))
2 голосов
/ 01 февраля 2009

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

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

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

return listOfStrings.Contains( startPath );

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

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

1 голос
/ 01 февраля 2009

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

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

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

1 голос
/ 01 февраля 2009

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

0 голосов
/ 05 ноября 2018

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

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
0 голосов
/ 25 апреля 2014
myList.Any(myString.Contains);
0 голосов
/ 01 февраля 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...