Home » Как проверить, содержит ли строка другую строку в Python
Как проверить, содержит ли строка другую строку в Python

Как проверить, содержит ли строка другую строку в Python

Если вы администрируете серверы, парсите логи или пишете скрипты автоматизации, то рано или поздно столкнётесь с задачей поиска подстроки в строке. Это одна из самых частых операций в Python, особенно при работе с конфигурационными файлами, анализе системных логов или обработке данных. В этой статье разберём все способы проверки вхождения строки в строку — от базовых до продвинутых методов с регулярными выражениями.

🔍 Базовые методы проверки вхождения строки

Самый простой и интуитивный способ — использовать оператор in. Он возвращает True или False в зависимости от того, найдена ли подстрока:

text = "nginx: [error] 404 - page not found"
if "error" in text:
    print("Найдена ошибка в логе")
    # Выводим: Найдена ошибка в логе

Оператор in чувствителен к регистру, поэтому "ERROR" и "error" — это разные строки. Для игнорирования регистра используйте lower():

log_line = "NGINX: [ERROR] Connection refused"
if "error" in log_line.lower():
    print("Найдена ошибка (регистр игнорируется)")

🛠️ Методы строк для поиска подстроки

Python предоставляет несколько встроенных методов для более гибкого поиска:

find() и rfind()

Возвращают позицию первого (или последнего) вхождения подстроки. Если подстрока не найдена, возвращается -1:

access_log = "192.168.1.100 - - [25/Dec/2023:10:00:00 +0000] GET /index.html 200"
ip_position = access_log.find("192.168")
if ip_position != -1:
    print(f"IP адрес найден на позиции: {ip_position}")
    # Выводим: IP адрес найден на позиции: 0

startswith() и endswith()

Проверяют, начинается ли или заканчивается ли строка определённой подстrokой:

server_name = "web-server-01.example.com"
if server_name.startswith("web-"):
    print("Это веб-сервер")

if server_name.endswith(".com"):
    print("Домен верхнего уровня: .com")

count()

Подсчитывает количество неперекрывающихся вхождений подстроки:

nginx_conf = """
server {
    server_name example.com;
    server_name www.example.com;
}
"""
server_count = nginx_conf.count("server_name")
print(f"Директива server_name встречается {server_count} раз")

🎯 Регулярные выражения для сложного поиска

Для более сложных паттернов поиска используйте модуль re:

import re

log_entry = "2023-12-25 10:30:45 [ERROR] Database connection failed"

# Поиск IP-адресов
ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
if re.search(ip_pattern, log_entry):
    print("Найден IP-адрес")

# Поиск временных меток
time_pattern = r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
if re.search(time_pattern, log_entry):
    print("Найдена временная метка")

⚡ Практические примеры для системного администрирования

Анализ логов Apache/Nginx

def analyze_access_log(log_line):
    errors = []
    
    # Проверка на ошибки 4xx и 5xx
    if " 4" in log_line or " 5" in log_line:
        if " 404 " in log_line:
            errors.append("404 Not Found")
        elif " 500 " in log_line:
            errors.append("500 Internal Server Error")
    
    # Проверка на подозрительные запросы
    suspicious_patterns = ["/admin", "/wp-admin", ".php", "sql"]
    for pattern in suspicious_patterns:
        if pattern in log_line.lower():
            errors.append(f"Подозрительный запрос: {pattern}")
    
    return errors

# Пример использования
log_lines = [
    '192.168.1.100 - - [25/Dec/2023:10:00:00 +0000] "GET /admin/login.php HTTP/1.1" 404 142',
    '10.0.0.1 - - [25/Dec/2023:10:00:01 +0000] "GET /index.html HTTP/1.1" 200 1234'
]

for line in log_lines:
    issues = analyze_access_log(line)
    if issues:
        print(f"Проблемы в строке: {issues}")

Проверка конфигурационных файлов

def check_nginx_config(config_content):
    checks = {
        "SSL включён": "ssl_certificate" in config_content,
        "Gzip сжатие": "gzip on" in config_content,
        "Кеширование": "expires" in config_content or "cache" in config_content,
        "Безопасные заголовки": "add_header" in config_content
    }
    
    return checks

# Чтение конфигурации
with open('/etc/nginx/nginx.conf', 'r') as f:
    config = f.read()

results = check_nginx_config(config)
for check, passed in results.items():
    status = "✅" if passed else "❌"
    print(f"{status} {check}")

📊 Сравнение производительности методов

Метод Скорость Гибкость Случаи использования
in Очень быстро Низкая Простая проверка вхождения
find() Быстро Средняя Нужна позиция подстроки
startswith()/endswith() Быстро Средняя Проверка префиксов/суффиксов
re.search() Медленно Очень высокая Сложные паттерны поиска

🚀 Автоматизация с помощью поиска подстрок

Мониторинг системных процессов

import subprocess

def check_service_status(service_name):
    try:
        result = subprocess.run(['systemctl', 'status', service_name], 
                              capture_output=True, text=True)
        
        if "active (running)" in result.stdout:
            return "running"
        elif "inactive" in result.stdout or "dead" in result.stdout:
            return "stopped"
        else:
            return "unknown"
    except Exception as e:
        return f"error: {e}"

# Проверка критических сервисов
critical_services = ['nginx', 'mysql', 'redis']
for service in critical_services:
    status = check_service_status(service)
    print(f"{service}: {status}")

Парсинг системной информации

def parse_memory_info():
    with open('/proc/meminfo', 'r') as f:
        meminfo = f.read()
    
    memory_stats = {}
    
    for line in meminfo.split('\n'):
        if 'MemTotal:' in line:
            memory_stats['total'] = int(line.split()[1])
        elif 'MemAvailable:' in line:
            memory_stats['available'] = int(line.split()[1])
        elif 'SwapTotal:' in line:
            memory_stats['swap_total'] = int(line.split()[1])
    
    return memory_stats

# Использование
mem_info = parse_memory_info()
print(f"Общая память: {mem_info['total']} KB")
print(f"Доступная память: {mem_info['available']} KB")

💡 Продвинутые техники и трюки

Множественный поиск с any() и all()

error_keywords = ['error', 'failed', 'exception', 'timeout']
warning_keywords = ['warning', 'deprecated', 'slow']

log_line = "Database connection failed with timeout error"

# Проверка на наличие любого из ключевых слов ошибки
has_error = any(keyword in log_line.lower() for keyword in error_keywords)
print(f"Содержит ошибку: {has_error}")

# Проверка на наличие всех ключевых слов
has_all_keywords = all(keyword in log_line.lower() for keyword in ['database', 'connection'])
print(f"Содержит все ключевые слова: {has_all_keywords}")

Кеширование результатов поиска

from functools import lru_cache

@lru_cache(maxsize=1000)
def cached_string_search(text, pattern):
    """Кеширование результатов поиска для часто используемых паттернов"""
    return pattern in text

# Особенно полезно при обработке больших логов
large_log = "..." * 10000  # Большой лог-файл
print(cached_string_search(large_log, "error"))  # Первый вызов
print(cached_string_search(large_log, "error"))  # Второй вызов - из кеша

🔗 Интеграция с другими инструментами

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

Интеграция с systemd для мониторинга

#!/usr/bin/env python3
import subprocess
import time

def monitor_journal_for_errors():
    """Мониторинг systemd journal на предмет ошибок"""
    cmd = ['journalctl', '-f', '-u', 'nginx', '--no-pager']
    
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                              stderr=subprocess.PIPE, text=True)
    
    for line in iter(process.stdout.readline, ''):
        if any(error in line.lower() for error in ['error', 'failed', 'critical']):
            print(f"🚨 Найдена ошибка: {line.strip()}")
            # Здесь можно добавить отправку уведомления

if __name__ == "__main__":
    monitor_journal_for_errors()

🧪 Тестирование и отладка

Всегда тестируйте ваши скрипты поиска на различных входных данных:

def test_log_analyzer():
    test_cases = [
        ("Normal request", "GET /index.html 200", False),
        ("404 error", "GET /missing.html 404", True),
        ("SQL injection attempt", "GET /search?q='; DROP TABLE users; --", True),
        ("Empty string", "", False),
        ("Unicode characters", "GET /файл.html 200", False)
    ]
    
    for description, log_line, expected_alert in test_cases:
        result = analyze_access_log(log_line)
        has_alert = len(result) > 0
        
        status = "✅" if has_alert == expected_alert else "❌"
        print(f"{status} {description}: {result}")

test_log_analyzer()

🎯 Заключение и рекомендации

Выбор метода поиска подстроки зависит от ваших конкретных задач:

  • Используйте in для простых проверок вхождения — это самый быстрый способ
  • Применяйте find() когда нужно знать позицию подстроки
  • Выбирайте startswith()/endswith() для проверки префиксов и суффиксов
  • Используйте регулярные выражения только для сложных паттернов — они медленнее
  • Кешируйте результаты при обработке больших объёмов данных

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

Дополнительные ресурсы:


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.

Leave a reply

Your email address will not be published. Required fields are marked