Урок курса
Поиск и форматирование строк
Python для новичковВ прошлом уроке мы разобрали методы, которые преобразуют строку: strip() убирал пробелы, replace() менял символы, split() и join() разбивали и собирали текст. Все эти методы что-то делали со строкой и возвращали новую. Сегодня добавляем другой класс методов — те, что не меняют строку, а ищут в ней информацию и возвращают либо позицию, либо количество, либо булево значение. Начнём с find().
Поиск подстроки: метод find() и ловушка с -1
Метод find() отвечает на вопрос: «С какой позиции начинается эта подстрока в строке?». Он возвращает индекс первого символа первого вхождения — или -1, если подстрока не найдена.
text = 'hello world'
print(text.find('world')) # 6
print(text.find('ll')) # 2
print(text.find('xyz')) # -1
Индекс 6 означает, что слово 'world' начинается на шестой позиции строки (считая с нуля). Если подстрока встречается несколько раз, find() возвращает позицию только первого вхождения.
Важная деталь: find() в отличие от index() никогда не бросает исключение. Если подстрока не найдена — просто вернёт -1. Это удобно, но тут же прячется ловушка.
Ловушка: проверять результат надо явно
Когда хочется узнать, есть ли подстрока в строке, интуитивно пишут так:
text = 'python is great'
if text.find('python'):
print('Нашли!')
Этот код работает неправильно. find('python') вернул 0 — подстрока 'python' стоит в самом начале строки, на нулевой позиции. Но 0 в Python — ложное значение, и условие if 0 не выполнится. Программа не выведет 'Нашли!', хотя подстрока есть.
Правильная проверка — явное сравнение с -1:
text = 'python is great'
if text.find('python') != -1:
print('Нашли!')
Теперь логика корректна: мы проверяем не «правдивость» числа, а конкретно «не равно минус одному».
Альтернативно для простой проверки наличия подстроки можно использовать оператор in:
if 'python' in text:
print('Нашли!')
in читается проще и не создаёт путаницы с нулевым индексом. Но find() незаменим, когда нужна именно позиция — например, чтобы вырезать часть строки или проверить, где именно встречается слово.
Подсчёт вхождений и проверка границ строки: count(), startswith(), endswith()
Три метода в этом разделе делают разные вещи и возвращают разные типы: count() даёт число (int), а startswith() и endswith() — булево значение (bool). Разберём каждый по очереди.
count() — сколько раз встречается подстрока
count(sub) считает, сколько раз подстрока sub встречается в строке. Вхождения не перекрываются — это важно понять на примере:
print('абракадабра'.count('а')) # 5
print('aaa'.count('aa')) # 1
print('hello world'.count('o')) # 2
print('python'.count('x')) # 0
Почему 'aaa'.count('aa') равно 1, а не 2? Метод находит первое вхождение 'aa' на позиции 0, «съедает» эти два символа, и следующий поиск начинается с позиции 2, где остался только один 'a'. Второго вхождения уже нет. Это стандартное поведение: count() не ищет перекрывающиеся совпадения.
Если подстрока не найдена — возвращает 0, а не -1. Это отличает его от find().
startswith() и endswith() — проверка начала и конца
Оба метода возвращают True или False. startswith(prefix) проверяет, начинается ли строка с указанного префикса, endswith(suffix) — заканчивается ли заданным суффиксом.
filename = 'report_2024.csv'
print(filename.startswith('report')) # True
print(filename.startswith('data')) # False
print(filename.endswith('.csv')) # True
print(filename.endswith('.xlsx')) # False
Эти методы удобнее, чем срезы, потому что код сразу говорит, что именно проверяется. Сравните:
# Через срез — неочевидно
if filename[:6] == 'report':
...
# Через startswith — читается как условие
if filename.startswith('report'):
...
Типичное применение endswith() — проверка расширения файла или формата строки перед обработкой:
def process_file(name):
if not name.endswith('.csv'):
print('Ожидается CSV-файл')
return
print(f'Обрабатываем файл: {name}')
process_file('data.csv') # Обрабатываем файл: data.csv
process_file('data.xlsx') # Ожидается CSV-файл
Оба метода чувствительны к регистру: 'Python'.startswith('py') вернёт False, потому что 'P' и 'p' — разные символы. Если нужна проверка без учёта регистра, строку надо предварительно привести к нижнему регистру через lower().
F-строки с выражениями внутри {}
F-строки знакомы с базового уровня: f'Привет, {name}!' подставляет значение переменной. Но фигурные скобки принимают не только имена переменных — внутри можно написать любое Python-выражение, и оно вычислится в момент создания строки.
Это значит, что можно вставлять арифметику, вызовы функций, обращения к методам строк и даже условные выражения — всё без предварительного сохранения в отдельную переменную.
name = 'Alice'
words = ['яблоко', 'груша', 'слива']
temperature = -5
print(f'Длина имени: {len(name)}') # Длина имени: 5
print(f'Заглавными: {name.upper()}') # Заглавными: ALICE
print(f'Два в восьмой: {2 ** 8}') # Два в восьмой: 256
print(f'Первый элемент: {words[0].upper()}') # Первый элемент: ЯБЛОКО
print(f'Мороз: {"да" if temperature < 0 else "нет"}') # Мороз: да
Каждое выражение в {} — это полноценный Python-код. Интерпретатор вычисляет его и подставляет результат в строку.
Несколько практических случаев, где это удобно:
- Когда нужно показать длину строки или списка прямо в сообщении —
f'Введено символов: {len(user_input)}'вместо двух строк кода. - Когда нужно привести значение к нужному виду на лету —
f'Логин: {username.lower().strip()}'. - Когда результат зависит от условия — тернарное выражение прямо внутри
{}.
Важная оговорка: выражение внутри {} не должно содержать обратную косую черту \. Если нужно использовать спецсимволы, лучше вычислить значение заранее и подставить переменную. Кавычки внутри {} можно использовать, но они должны отличаться от кавычек самой f-строки — как в примере с тернарным выражением выше, где строка обёрнута в одинарные кавычки, а внутри использованы двойные.
Эта возможность пригодится в следующем разделе: функция форматирования отчётной строки будет собирать результаты find(), count(), startswith() прямо внутри f-строки, без промежуточных переменных для каждого значения.
Функция форматирования отчётной строки: сборка всего вместе
Теперь, когда все инструменты разобраны по отдельности, соберём их в одну функцию. Задача: принять текст и ключевое слово, прогнать через несколько методов поиска и вернуть готовую отчётную строку.
Вот пошаговая логика:
Объявить функцию с параметрами — текст и ключевое слово.
Найти позицию ключевого слова через
find().Проверить, найдена ли подстрока (
position != -1), и вернуть соответствующее сообщение.Если найдена — вычислить остальные значения (
count(),startswith(),endswith()) и собрать результат в f-строку.Вернуть строку через
return.
Шаг 3 — ключевой. Вспомним из первого раздела: find() возвращает -1, когда подстрока не найдена. Если просто подставить -1 в строку 'найдено на позиции: -1' — это семантически бессмысленно и сбивает с толку. Правильнее вернуть отдельное сообщение:
def format_report(text, keyword):
position = text.find(keyword)
if position == -1:
return f'Слово "{keyword}" не найдено в строке'
occurrences = text.count(keyword)
starts = text.startswith(keyword)
ends = text.endswith(keyword)
return (
f'Слово "{keyword}" найдено на позиции: {position}; '
f'встречается {occurrences} раз(а); '
f'строка начинается с него: {starts}; '
f'строка заканчивается им: {ends}'
)
print(format_report('python is great, python wins', 'python'))
# Слово "python" найдено на позиции: 0; встречается 2 раз(а); строка начинается с него: True; строка заканчивается им: False
print(format_report('hello world', 'xyz'))
# Слово "xyz" не найдено в строке
Ранний return при position == -1 — это стандартный приём: как только стало ясно, что продолжать нет смысла, функция сразу возвращает результат. Остальной код выполняется только тогда, когда подстрока точно найдена.
Если контекст не требует отдельной обработки отсутствия подстроки (например, нужен только счётчик вхождений), выражения можно вынести прямо в {} f-строки без промежуточных переменных:
def format_report_simple(text, keyword):
return (
f'Слово "{keyword}" встречается {text.count(keyword)} раз(а); '
f'строка начинается с него: {text.startswith(keyword)}; '
f'строка заканчивается им: {text.endswith(keyword)}'
)
Этот компактный вариант не включает позицию и не обрабатывает случай «не найдено» — он подходит только тогда, когда отсутствие подстроки не нужно выделять отдельно. Для большинства реальных задач полный вариант с проверкой надёжнее.
Обратите внимание: функция не печатает ничего сама — она возвращает строку. Что с ней делать дальше — решает вызывающий код: напечатать, записать в файл, сравнить с эталоном. Это стандартный принцип: функция отвечает за формирование результата, а не за его судьбу.
Такой паттерн — принять данные, проверить наличие ключевого элемента, обработать и вернуть отформатированную строку — встречается постоянно: при генерации отчётов, логов, сообщений об ошибках. Когда в уроке про работу с файлами придёт время разбирать строки, прочитанные из текста, эта же структура функции окажется готовым шаблоном.
Задача 1
Что вернёт выражение?
'абракадабра'.find('кад')
Задача 2
Что выведет следующий код? Запишите результаты двух вызовов print в одной строке, разделив их одним пробелом.
text = 'banana'
print(text.find('an'))
print(text.find('xyz'))
Задача 3
Что вернёт выражение 'mississippi'.count('ss')? Введите только число.
Задача 4
Что вернёт выражение 'Python'.startswith('py')?
Задача 5
Программист хочет проверить, что строка text не содержит слова «ошибка». Какой вариант кода делает это правильно?
Задача 6
Какую строку создаст выражение f'{name.upper()} — {len(name) + 1}', если name = 'кот'?
Задача 7
Какое из следующих выражений является синтаксически корректным внутри {} f-строки в Python?
Задача 8
Нужно написать функцию, которая принимает имя файла и возвращает True, если файл является изображением (оканчивается на .png, .jpg или .gif). Какой вариант реализует это корректно?
Задача 9
Отчёт по строке и ключевому слову
Напишите функцию format_report(text, keyword), которая формирует строку-отчёт по двум строкам.
Сделайте так:
- переведите и
text, иkeywordв нижний регистр; - найдите позицию первого вхождения
keywordвtextметодомfind(); - посчитайте количество вхождений
keywordметодомcount(); - проверьте методом
startswith(), начинается ли текст с этого слова; - если слово не найдено, верните строку
«<keyword>»: не найдено; - иначе верните строку вида
«<keyword>»: позиция X, вхождений Y, в начале: True/False.
Для вывода используйте именно f-строку с выражениями в {}.
Функция должна возвращать строку, а не печатать её.
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Задачи к уроку
- Поиск и форматирование строк — задача по Python
Какой вариант реализует это корректно? Проверьте ответ в задаче из бесплатного курса Python.
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
