Занятие 3
Регулярные выражения¶
Работа с файлами¶
Пример: создайте файл input.txt, скопируйте туда таблицу с числами ниже:
1 2 3
4 5 6
По умолчанию файл открывает в режиме "чтения", т. е. внести изменения в него не получится. Есть следующие режимы открытия файлов:
'r' открытие на чтение (является значением по умолчанию).
'w' открытие на запись, содержимое файла удаляется, если файла не существует, создается новый.
'x' открытие на запись, если файла не существует, иначе исключение.
'a' открытие на дозапись, информация добавляется в конец файла.
'b' открытие в двоичном режиме.
'+' открытие на чтение и запись
with open("input.txt", "r") as f:
print(f.read())
with open("input.txt", "r") as f:
s = f.read()
print(s)
s, type(s)
Так выглядит внутреннее представление строки s. Когда мы вызываем функцию print символ переноса строки \n переводит курсор на новую строку:
print("1\n2")
with open("input.txt", "r") as f:
a = f.readlines()
print(a)
Напомним, что rstrip() удаляет пробельные элементы из конца строки, в частности, символ переноса строки
with open("input.txt", "r") as f:
a = f.readlines()
a = list(map(lambda x: x.rstrip().split(), a))
print(a)
with open("input.txt", "r") as f:
a = f.readlines()
a = list(map(lambda x: [int(y) for y in x.rstrip().split()], a))
print(a)
Или проще:
with open("input.txt", "r") as f:
a = f.readlines()
a = list(map(lambda x: x.rstrip().split(), a))
for i in range(len(a)):
for j in range(len(a[i])):
a[i][j] = int(a[i][j])
print(a)
Пример: в созданный файл запишем вместо чисел, которые там были, новые числа, равные квадратам исходных
b = [[a[i][j] ** 2 for j in range(len(a[i]))] for i in range(len(a))]
print(b)
with open("output.txt", "w") as f:
f.writelines([" ".join(x) for x in b])
b = [[str(x) for x in b[i]] for i in range(len(b))]
with open("output.txt", "w") as f:
f.writelines([" ".join(x) for x in b])
Посмотрите, что записалось в файл. Не совсем то, что нужно, не хватает символов переноса строки
b = [[str(x) for x in b[i]] for i in range(len(b))]
with open("output.txt", "w") as f:
f.writelines([" ".join(x) + "\n" for x in b])
Файлы можно открывать не только на запись, но и на дозапись. Обратите внимание, что в предыдущей ячейке предыдущее сожержимое файла стерлось запуском open, для этого используем флажок a вместо w
b = [[str(x) for x in b[i]] for i in range(len(b))]
with open("output.txt", "a") as f:
f.write("done!")
Пример: дана таблица результатов олимпиады в формате "Фамилия Имя Класс Балл". Нужно скопировать ее в текстовый файл и считать в двумерный массив. Задача - определить фамилию и имя победителя олимпиады. Если их несколько, вывести первого в лексикографическом порядке.
Решение: считаем данные в двумерный список. Отсортируем его по убыванию балла и по возрастанию ФИ.
Таблица:
Иванов Сергей 9 90
Сергеев Петр 10 85
Петров Иван 11 90
with open("input.txt", "r", encoding="utf-8") as f:
a = list(map(lambda x: x.rstrip().split(), f.readlines()))
print(a)
print(sorted(a, key = lambda x: (-int(x[3]), x[0], x[1])))
print(" ".join(sorted(a, key = lambda x: (-int(x[3]), x[0], x[1]))[0][:2]))
Регулярное выражение — это строка, задающая шаблон поиска подстрок в тексте. Одному шаблону может соответствовать много разных строчек.
В состав регулярных выражений или кратко регулярка входят обычные символы и специальные командные последовательности. К примеру, \d задаёт любую цифру, а \d+ — задает любую последовательность из одной или более цифр.
Любая строка (в которой нет символов .^$*+?{}[]|()) сама по себе является регулярным выражением. Так, выражению 'Abcd' будет соответствовать строка 'Abcd' и только она. В регулярных выражениях имеет значение регист, т. е. они являются регистрозависимыми. Это значит, что строка 'abcd' (с маленькой буквы) уже не будет соответствовать выражению выше.
Cпецсимволы .^$*+?{}[]|() в регулярках являются управляющими конструкциями. Если эти символы нужно использовать просто как символы, то требуется их экранирование. Для этого нужно поставить перед ними знак .
Рассмотрим шаблоны, соответствующие одному символу:
. - один любой символ за исключением новой строки \n. Пример: М.Т. -> МФТИ, МГТУ
\d - любая цифра. Пример: F\d\d -> F54, FF60
\D - любой символ, кроме цифры. Пример: 926\D123 -> 926)123, 1926-1234, a926b123
\s - лЛюбой пробельный символ (пробел, табуляция, конец строки и т.п.). Пример: экз\sамен -> экз амен, экз амен
\S - любой непробельный символ. Пример: \S101-> !10145, ит101, b101
\w - любая буква (то, что может быть частью слова), а также цифры и _. Пример: \w\w\w -> Год, f_3, qwert
\W - любая не-буква, не-цифра и не подчёркивание. Пример: abc\W -> abc!, abc=
[..] - один из символов в скобках, а также любой символ из диапазона a-b. Пример: [0-9][0-9A-Fa-f] -> 12, 1F, 4B
[^..] - любой символ, кроме перечисленных. Пример: <[^>]> -> <1>, , но вот <>> под этот шаблон не подойдёт
^ — начало текста (строки)
$ — конец текста (строки)
Примеры исключений символов с помощью ^ внутри []:
[^0-9] — любой символ, кроме цифр
[^ёЁ] — любой символ, кроме буквы «ё»
[^а-в8] — любой символ, кроме букв «а», «б», «в» и цифры 8
Квантификаторы (указание количества повторений)¶
{n} - Ровно n повторений
{m,n} - От m до n повторений включительно
{m,} - Не менее m повторений
{,n} - Не более n повторений
? - Ноль или одно вхождение, синоним {0,1}
* - Ноль или более, синоним {0,}
+ - Одно или более, синоним {1,}
{,n} - Не более n повторений
Пример:
Допустим нужно проверить корректность введённого в input-поле e-mail. Это можно сделать с помощью регулярных выражений.
r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)+$'
Регулярные выражения позволяют:
определить нужный формат, например телефонного номера или email-адреса;
разбить строки на подстроки;
проводить поиск, замену и извлечение символов;
и т. д.
Дополнительные примеры по регулярным выражениям: https://habr.com/ru/articles/545150/
Для работы с регулярками в питоне есть модуль re. Перед началом работы его нужно импортировать:
import re
Метод re.match(pattern, string) ищет по заданному шаблону в начале строки.
result = re.match(r'.y', 'Python was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result)
Искомая подстрока найдена, но чтобы вывести её содержимое, применим метод group(). Здесь используется «r» перед строкой шаблона, чтобы показать, что это «сырая» строка в Python.
result = re.match(r'.y', 'Python was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result.group(0))
Есть методы start() и end() для того, чтобы узнать начальную и конечную позицию найденной строки.
result = re.match(r'.y', '.ython was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result.start())
print(result.end())
re.search(pattern, string) похож на match(), но ищет не только в начале строки. Этот метод ищет по всей строке, но возвращает только первое найденное совпадение.
result = re.search(r'Py', 'Python was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result.group(0))
Для того, чтобы вернуть список всех найденных совпадений есть метод re.findall(pattern, string).
result = re.findall(r'Py', 'Python was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result)
re.split(pattern, string, [maxsplit=0]) разделяет строку по заданному шаблону.
result = re.split(r'Py', 'Python was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result)
Метод re.sub(pattern, repl, string) ищет шаблон в строке и заменяет его на указанную подстроку. Если шаблон не найден, строка остается неизменной.
result = re.sub(r'Py', 'Cy', 'Python was created in the early 1990s by Guido van Rossum. Python is a high-level, general-purpose programming language.')
print(result)
Можно собрать регулярное выражение в отдельный объект, который может быть использован для поиска. Для этого есть метод re.compile(pattern, repl, string).
pattern = re.compile(r'.12')
result1 = pattern.findall('В 12 часов началось собрание.')
print(result1)
result2 = pattern.findall('В 12 часов началось собрание, а в 12:30 завершилось.')
print(result2)
Пример:
Вернуть первое слово из строки.
result = re.findall(r'^\w+', 'Python is a high-level, general-purpose programming language.')
print(result)
Пример:
Вернуть первые два символа каждого слова.
Вариант 1: используя \w, вытащить два последовательных символа, кроме пробельных, из каждого слова:
result = re.findall(r'\w\w', 'Python is a high-level, general-purpose programming language.')
print(result)
Вариант 2: вытащить два последовательных символа, используя символ границы слова (\b):
result = re.findall(r'\b\w.', 'Python is a high-level, general-purpose programming language.')
print(result)
Пример:
Вернуть домены из списка email-адресов.
result = re.findall(r'@\w+', 'abc.test@gmail.com, xyz@test.in, test1@gmail.com, first.1@rest.biz')
print(result)
Как видно «.com», «.in» и т. д. не попали в результат. Исправим это.
result = re.findall(r'@\w+.\w+', 'abc.test@gmail.com, xyz@test.in, test1@gmail.com, first.1@rest.biz')
print(result)
Другой вариант решения:
result = re.findall(r'@\w+.(\w+)', 'abc.test@gmail.com, xyz@test.in, test1@gmail.com, first.1@rest.biz')
print(result)
Упражнение 1. Считать через input строку, состоящую из нескольких слов. Извлечь слова, начинающиеся на гласную.
Упражнение 2. Проверить формат телефонного номера. Номер должен быть длиной 10 знаков и начинаться с 8 или 9. Есть список телефонных номеров, которые вводятся с клавиатуры и нужно проверить их корректность.
Упражнение 3. Нужно извлечь информацию из html-файла, заключенную между <td> и </td>, кроме первого столбца с номером. Также будем считать, что html-код содержится в строке.
Пример содержимого html-файла:
1NoahEmma2LiamOlivia3MasonSophia4JacobIsabella5WilliamAva6EthanMia7MichaelEmily
Упражнение 4. В России применяются регистрационные знаки нескольких видов. Общего в них то, что они состоят из цифр и букв. Причём используются только 12 букв кириллицы, имеющие графические аналоги в латинском алфавите — А, В, Е, К, М, Н, О, Р, С, Т, У и Х.
У частных легковых автомобилях номера — это буква, три цифры, две буквы, затем две или три цифры с кодом региона. У такси — две буквы, три цифры, затем две или три цифры с кодом региона. Есть также и другие виды, но в этой задаче они не понадобятся.
Вам потребуется определить, является ли последовательность букв корректным номером указанных двух типов, и если является, то каким.
На вход даются строки, которые претендуют на то, чтобы быть номером. Определите тип номера. Буквы в номерах — заглавные русские. Маленькие и английские для простоты можно игнорировать.
Примеры:
С227НА777 -> Private
КУ22777 -> Taxi
М227К19У9 -> Fail
Т22В7477 -> Fail
Упражнение 5. У вас есть текст сообщения, в котором не везде указано правильное время. Требуется это исправить. Для этого нужно заменить все вхождения времени на строку (TBD). Время — это строка вида HH:MM:SS или HH:MM, в которой HH — число от 00 до 23, а MM и SS — число от 00 до 59.
Пример:
Ввод:
В 09:00 начнётся лекция, а в 09:00:01 некоторые студенты только собираются на пары. В 25:50 пар нет!
Вывод:
В (TBD) начнётся лекция, а в (TBD) некоторые студенты только собираются на пары. В 25:50 пар нет!
Упражнение 6. В одной организации потребовалось запутать финансовую документацию. Но важно, чтобы это было обратимо. В организации придумали вариант заменить каждое целое число (последовательность цифр) на его куб. Напишите код для предложенного алгоритма.
Пример:
Ввод:
Было закуплено 12 единиц техники по 410.37 рублей.
Вывод:
Было закуплено 1728 единиц техники по 68921000.50653 рублей.