Проверьте, содержатся ли несколько строк в другой строке
Вопрос: Как проверить, содержится ли хотя бы одна из строк в массиве в другой строке?
У меня есть массив строк, и я хочу проверить, присутствует ли хотя бы одна из этих строк в другой строке. Вот пример:
a = ['a', 'b', 'c']
s = "a123"
if a in s:
print("Некоторые строки найдены в s")
else:
print("Строки не найдены в s")
Как я могу заменить строку if a in s:
на что-то, что даст правильный результат?
5 ответ(ов)
any()
— это, безусловно, лучший способ, если вам нужно получить только True
или False
. Однако, если вас интересует, какие именно строки совпадают, вы можете воспользоваться несколькими подходами.
Если вам нужно найти первое совпадение (с False
в качестве значения по умолчанию):
match = next((x for x in a if x in a_string), False)
Если вам нужно получить все совпадения (включая дубликаты):
matches = [x for x in a if x in a_string]
Если вы хотите получить все совпадения без дубликатов (не учитывая порядок):
matches = {x for x in a if x in a_string}
Если нужно получить все совпадения без дубликатов в правильном порядке:
matches = []
for x in a:
if x in a_string and x not in matches:
matches.append(x)
Вы можете использовать regex
для добавления разнообразия в решение вашей задачи:
import re
if any(re.findall(r'a|b|c', str, re.IGNORECASE)):
print('Возможные совпадения благодаря regex')
else:
print('Совпадений нет')
Если ваш список слишком длинный, вы можете сделать это следующим образом:
any(re.findall(r'|'.join(a), str, re.IGNORECASE))
Это позволит вам эффективно проверять наличие совпадений, игнорируя регистр. Не забудьте заменить str
на вашу переменную, содержащую строку, с которой вы работаете.
Чтобы перебрать элементы списка a
и проверить, содержится ли каждый из них в строке a_string
, вы можете использовать следующий код:
a = ['a', 'b', 'c']
a_string = "a123"
found_a_string = False
for item in a:
if item in a_string:
found_a_string = True
break # Выход из цикла, если найдено совпадение
if found_a_string:
print("found a match")
else:
print("no match found")
В этом коде мы проходим по каждому элементу списка a
и проверяем, содержится ли он в строке a_string
. Если хотя бы один элемент найден в строке, переменная found_a_string
устанавливается в True
, и мы прерываем цикл. После завершения цикла мы проверяем значение found_a_string
, чтобы определить, было ли найдено совпадение, и выводим соответствующее сообщение.
Вам нужно было реализовать это в контексте критичной к производительности среды, поэтому я провел бенчмарки всех возможных вариантов на Python 3.11. Вот результаты:
words = ['test', 'èk', 'user_me', '<markup>', '[^1]']
def find_words(words):
for word in words:
if "_" in word or "<" in word or ">" in word or "^" in word:
pass
def find_words_2(words):
for word in words:
for elem in [">", "<", "_", "^"]:
if elem in word:
pass
def find_words_3(words):
import re
for word in words:
if re.search(r"\_|\<|\>|\^", word):
pass
def find_words_4(words):
import re
for word in words:
if re.match(r"\S*(\_|\<|\>|\^)\S*", word):
pass
def find_words_5(words):
for word in words:
if any(elem in word for elem in [">", "<", "_", "^"]):
pass
def find_words_6(words):
for word in words:
if any(map(word.__contains__, [">", "<", "_", "^"])):
pass
Результаты тестирования:
> %timeit find_words(words)
351 ns ± 6.24 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit find_words_2(words)
689 ns ± 15.4 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit find_words_3(words)
2.42 µs ± 43.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
> %timeit find_words_4(words)
2.75 µs ± 146 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
> %timeit find_words_5(words)
2.65 µs ± 176 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
> %timeit find_words_6(words)
1.64 µs ± 28.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
- Наиболее простой метод с использованием цепочки
or
(функция 1) показывает наилучшие результаты. - Базовая итерация по каждому элементу (функция 2) как минимум на 50% быстрее, чем использование
any()
, при этом даже регэкспы работают быстрее, чем базовоеany()
, поэтому не понимаю, зачем он вообще нужен. К тому же синтаксис абсолютно алгоритмический, и любой программист сможет понять, что он делает, даже без опыта с Python. re.match()
ищет только по шаблонам, начинающимся с начала строки (что может вызвать путаницу, если вы привыкли к регэкспам в PHP/Perl), поэтому чтобы он работал как в PHP/Perl, нужно использоватьre.search()
или модифицировать регэксп, что повлечет за собой потерю производительности.
Если список подстрок известен на этапе программирования, не стесняйтесь использовать неприглядный метод с цепочкой or
. В противном случае лучше использовать простой цикл for
по списку подстрок. any()
и регулярные выражения — это потеря времени в этом контексте.
Для более практической задачи (определение, является ли файл изображением, исходя из его расширения в списке):
def is_image(word: str) -> bool:
if any(ext in word for ext in [
".bmp", ".jpg", ".jpeg", ".jpe", ".jp2", ".j2c", ".j2k",
".jpc", ".jpf", ".jpx", ".png", ".ico", ".svg", ".webp",
".heif", ".heic", ".tif", ".tiff", ".hdr", ".exr", ".ppm",
".pfm", ".nef", ".rw2", ".cr2", ".cr3", ".crw", ".dng",
".raf", ".arw", ".srf", ".sr2", ".iiq", ".3fr", ".dcr",
".ari", ".pef", ".x3f", ".erf", ".raw", ".rwz"]):
return True
return False
IMAGE_PATTERN = re.compile(r"\.(bmp|jpg|jpeg|jpe|jp2|j2c|j2k|jpc|jpf|jpx|png|ico|svg|webp|heif|heic|tif|tiff|hdr|exr|ppm|pfm|nef|rw2|cr2|cr3|crw|dng|raf|arw|srf|sr2|iiq|3fr|dcr|ari|pef|x3f|erf|raw|rwz)")
Результаты bенчмарков:
> %timeit is_image("DSC_blablabla_001256.nef") # найдено
536 ns ± 18.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit is_image("DSC_blablabla_001256.noop") # не найдено
923 ns ± 43.8 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit IMAGE_PATTERN.search("DSC_blablabla_001256.nef")
221 ns ± 24.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit IMAGE_PATTERN.search("DSC_blablabla_001256.noop") # не найдено
207 ns ± 4.3 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit any(ext in "DSC_blablabla_001256.nef" for ext in extensions) # найдено
1.53 µs ± 30.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
> %timeit any(ext in "DSC_blablabla_001256.noop" for ext in extensions) # не найдено
2.2 µs ± 25.1 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
При большом количестве проверяемых элементов регэкспы действительно работают быстрее и более читаемы (в очередной раз…), чем цепочка or
. any()
все еще показывает худшие результаты.
Эмпирические тесты показывают, что порог производительности приходится на 9 элементов для проверки:
- меньше 9 элементов — метод с цепочкой
or
быстрее, - больше 9 элементов —
regex search()
быстрее, - на ровно 9 элементах оба варианта работают примерно за 225 ns.
Ваш код на Python выполняет проверку, содержатся ли какие-либо элементы из списка a
в строке str
. Вот исправленный и более читаемый вариант с использованием Python 3:
a = ['a', 'b', 'c']
s = "a123"
a_match = [match for match in a if match in s]
if a_match: # Проверяем, есть ли совпадения
print("Некоторые строки найдены в str")
else:
print("Строки не найдены в str")
Здесь я произвел следующие изменения:
- Переименовал переменную
str
вs
, посколькуstr
является встроенной функцией в Python и не рекомендуется переопределять её. - Упростил проверку на наличие совпадений, заменив
if True in a_match
на простоif a_match
, так как еслиa_match
непустой, это означает, что найдено хотя бы одно совпадение. - Использовал функцию
print()
с круглой скобкой, чтобы соответствовать синтаксису Python 3.
Есть ли в Python метод подстроки 'contains' для строк?
Как получить последний элемент списка?
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Самый быстрый способ проверить наличие значения в списке
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе