Строки и работа с текстом
Ученик вводит «Иванов». Программа отвечает «такого участника нет». В списке Иванов есть, на экране написано так же, буква в букву, и ученик, естественно, считает, что сломалась программа. Он ошибается, а программа права: то, что пришло с клавиатуры, не равно тому, что лежит в списке. Нужно найти, чем именно, и для этого есть последовательность из нескольких проверок, которую удобно пройти один раз и дальше применять как привычку.
Для нас пример учебный: список участников и поиск по фамилии, а затем номер телефона, который люди пишут как попало. Строка в программе — это последовательность символов, у неё есть длина и номера позиций, как у списка из статьи про списки. Но у строки есть и своя специфика, из-за которой ошибки с текстом самые неочевидные: часть различий глазу не видна.
Увидеть, что ввели
Сначала стоит убедиться, что дело именно в сравнении: бывает, что верно всё, кроме самого ввода. Две строки: name = input(), затем print(name == "Иванов"). Выводится False. Почему, догадаться глазами нельзя, поэтому спрашиваем у программы, что именно лежит в переменной: print(repr(name)) и print(len(name)).
Функция repr показывает строку вместе с кавычками, и это решает дело: вместо Иванов на экране 'Иванов ' с пробелом перед закрывающей кавычкой. А len вместо шести выводит семь. Ученик не заметил, что нажал пробел перед Enter; так часто бывает, особенно у тех, кто привык к клавиатуре телефона. Невидимый символ стал частью строки.
Один случай бывает ещё коварнее: вместо обычного пробела может оказаться неразрывный, который попадает в текст при копировании из сайтов и документов. На экране он неотличим от обычного, а repr показывает его как \xa0. Это полезный инструмент: если есть подозрение на невидимые символы, repr спрашивают первым.
Убрать лишнее по краям
Лечится пробел методом strip(): он отбрасывает пробелы и переводы строки с обоих концов. Поэтому читать фамилию лучше сразу так: name = input().strip(). Теперь строка совпадает по длине с эталоном, и лишние пробелы по краям перестают быть проблемой. Заодно strip убирает и неразрывный пробел, а это значит, что обработка работает, даже когда глаз её не увидел.
Тут стоит предупредить об ошибке, которую делают почти все. Если написать name.strip() отдельной строкой, ничего не произойдёт: переменная осталась прежней, с пробелом. Методы строк не меняют строку на месте, они возвращают новую, и результат нужно куда-то положить, то есть присвоить. Python не даёт и изменить строку по номеру, как список: попытка name[0] = "и" остановится с ошибкой TypeError. Строка неизменяема, и это принципиальная разница между ней и списком.
Регистр
Пробел снят, а сравнение всё равно ложно, если ученик набрал «иванов» с маленькой буквы или «ИВАНОВ» в режиме заглавных. Для компьютера «И» и «и» разные символы. Решение — привести обе стороны к одному регистру перед сравнением: name = input().strip().lower(), и в списке тоже хранить или сравнивать в нижнем регистре.
Методы здесь выстроены цепочкой: каждый работает с результатом предыдущего, и читать запись надо слева направо. Порядок strip и lower в нашем случае не важен, хотя в других цепочках он может решать дело. Существует и метод casefold(), он чуть агрессивнее: учитывает такие случаи, как немецкую «ß». Для русского текста разницы вы не увидите, и по умолчанию хватает lower.
Пользователя нельзя просить вводить правильно. Любая надпись вида «введите фамилию с заглавной, без пробелов» — признак того, что приведение к одному виду автор переложил на человека, и человек будет ошибаться. Программа должна принимать то, что ей дали, и сама выравнивать. Исключение составляют лишь учебные работы, где как раз важно показать, что ввод бывает неверным.
Иногда нужно проверить вхождение: есть ли в строке такая-то часть. Для неё служит слово in: "ван" in "иванов" даёт True. Удобно, когда ищут по первым буквам, и опасно, когда ищут короткий кусок: «ван» найдётся и в «Иванов», и в «Ивановский», и в «Иван», и в какой-нибудь «Вановой». Выбор между «равно» и «содержит» — это вопрос к задаче, язык тут ни при чём, и стоит задавать его раньше, чем писать код.
Прежде чем идти дальше, полезно собрать набор проверочных вводов и прогнать по ним готовую программу: «Иванов», « Иванов» с пробелом спереди, «иванов», «ИВАНОВ» и вариант с пробелом сзади. Все пять должны давать одинаковый ответ. Если хотя бы один отличается, значит, шаг пропущен, и по выводу repr видно, какой.
Первая буква и вывод для человека
Бывает, что имя нужно вернуть пользователю в красивом виде. Первую букву строки даёт name[0], остальные символы начиная со второго даёт name[1:], и, значит, привести слово к виду «Иван» можно вручную: name[0].upper() + name[1:].lower(). Готовый capitalize() делает ровно это, и ручной вариант нужен лишь затем, чтобы увидеть: строка ведёт себя как список символов, с теми же номерами от нуля.
Вставлять значения в текст удобнее всего через f-строку: print(f"Участник {name} найден"). Перед открывающей кавычкой стоит буква f, а в фигурных скобках записывается то, что надо подставить. Это короче, чем склейка через плюс, и не требует преобразовывать числа в текст вручную, как это было с типами данных.
Фамилия и имя целиком
Теперь реальная задача: человек вводит «иванов иван», с тремя пробелами посередине. Метод split() без аргументов режет строку по любому количеству пробелов и возвращает список из двух слов: parts = text.split() даёт ["иванов", "иван"]. Каждое слово можно привести к виду «Иванов» методом capitalize() или title().
Между ними есть тонкость, которая проявляется на двойных фамилиях. Фамилия «петров-водкин» после capitalize() станет «Петров-водкин», потому что метод делает заглавной одну первую букву и опускает остальные, а после title() станет «Петров-Водкин»: заглавные ставятся после каждого дефиса. Для имён и фамилий обычно нужен второй, но проверять надо на реальных примерах: самые простые ничего не покажут.
Отдельная история с буквой «ё». В фамилии Фёдоров её нередко пишут как «е», и при поиске по списку это даёт ложное «не найдено». Надёжное решение для сравнения такое: заменить «ё» на «е» с обеих сторон, например name.replace("ё", "е"), а в самом хранилище букву не терять, иначе человек увидит свою фамилию в искажённом виде. Это тот случай, когда нормализовать нужно только то, что идёт на сравнение.
Номер телефона
Номера пишут тремя способами сразу: «+7 (916) 123-45-67», «8 916 123 45 67», «89161234567». Для человека это один номер, для программы три разных строки. Простейший способ свести их к одному: оставить только цифры, digits = "".join(c for c in phone if c.isdigit()) (запись означает «собрать строку из тех символов, которые являются цифрами»). Для первого номера выйдет «79161234567», для третьего «89161234567».
Остаётся различие в первой цифре. Для российских номеров 8 и +7 означают одно и то же, и приводят к одному виду: if digits.startswith("8"): digits = "7" + digits[1:]. Выражение digits[1:] — срез, всё, кроме первого символа, как в теме про списки. Обратно для показа человеку номер собирают срезами: "+7 (" + digits[1:4] + ") " + digits[4:7] + "-" + digits[7:9] + "-" + digits[9:] даёт привычное «+7 (916) 123-45-67». Хранится при этом одна нормальная форма, а вид для экрана делается по требованию, и менять оформление можно в одном месте, не трогая данные.
Для других стран правила свои, и вставлять здесь готовые числа не стоит, их нужно уточнять по задаче.
Куда важнее вопрос о том, во что превращать номер. Хранить его надо строкой, а не числом: арифметики с номером никто не делает, зато число потеряет ведущий ноль, если он есть, и знак плюс. Соблазн сделать int возникает, потому что номер состоит из цифр, и на этом соблазне обычно и ломаются первые учебные записные книжки.
Откуда это на экзамене
Строки встретятся при чтении файлов: каждая строка файла приходит с символом перевода строки на конце, и его убирают тем же strip(). Подробнее о файлах рассказано в статье про работу с файлами. А на экзамене ответ вводят в специальную программу, и формат ответа задан в условии: лишний пробел или регистр там могут стоить балла. Устройство экзамена — в статье о структуре ЕГЭ по информатике; какие задания первой части связаны с обработкой текста, видно по карте первой части и по демоверсии своего года.
При упорядочении списка фамилий строки тоже ведут себя неожиданно: порядок по умолчанию не совпадает с классным журналом, подробности в статье про сортировки и поиск.
Когда в программе много строк, которые надо искать по имени, вместо списка удобнее словарь; о нём в статье про словари и множества. Общее место темы среди остальных — в обзоре школьной информатики.
Начать поиск:
- репетиторы по информатике, все направления
- репетиторы по информатике для подготовки к ЕГЭ
- репетиторы по информатике для 9 класса
Вопросы и ответы
Чем lower() отличается от casefold()? Для русского текста ничем. Casefold аккуратнее с редкими буквами других алфавитов.
Почему строку нельзя изменить по номеру, а список можно? Строки в Python неизменяемы: любой метод создаёт новую строку. Чтобы «изменить» символ, строку собирают заново, например из среза до нужной позиции, новой буквы и среза после неё. На практике это нужно редко, чаще достаточно replace.
Как показать репетитору строку, в которой я не вижу ошибки, если он не видит мой экран? Демонстрации экрана на платформе нет, а невидимый символ не покажет и скриншот: его не видно и на нём. Поэтому вместе с файлом программы отправляйте в чат отдельной строкой вывод repr с вашим вводом: по нему репетитор увидит и обычный пробел, и неразрывный. Остальные подробности про чат и файлы — в обзоре школьной информатики.
Что делать, если пробелы внутри слова или между словами? Если речь о лишних пробелах между словами, split() без аргументов их сворачивает. Если нужно убрать все, включая внутри, подойдёт replace(" ", "").
Нужны ли регулярные выражения? Для школьных задач нет. Номер телефона и фамилию можно привести к виду методами строк, как показано выше. Регулярные выражения разумно оставить на потом, когда обычные приёмы покажутся громоздкими: большинству школьников этого момента ждать долго.
Оставить заявку
- Татьяна Попова — Информатика и олимпиадное программирование · от 3 000 ₽
- Артём Игоревич Соловьёв — Информатика и математика: ОГЭ и ЕГЭ от практикующего разработчика · от 2 100 ₽
- Илья Германович Воронцов — Информатика: ЕГЭ, олимпиады и алгоритмы для студентов · от 1 700 ₽