На этом шаге мы рассмотрим одно из возможных решений указанной задачи.
Этот однострочник демонстрирует еще одно применение регулярных выражений на практике. В данном случае представьте себя финансовым аналитиком. Ваша
компания планирует приобрести другую, и вам поручили прочитать ее финансовые отчеты. Особенно вас интересуют все суммы в долларах. Конечно, вы
можете просмотреть весь документ вручную, но это утомительная работа и вы не хотели бы тратить на нее самое продуктивное время дня. Поэтому вы решили
написать небольшой сценарий на Python. Но как лучше всего это сделать?
К счастью, вы уже прочли руководство по регулярным выражениям, поэтому вместо того чтобы тратить огромное количество времени на написание собственного, очень большого и чреватого ошибками средства синтаксического разбора на Python, вы решили воспользоваться более аккуратной реализацией на основе регулярных выражений - мудрое решение. Но прежде чем углубиться в решение поставленной задачи, обсудим еще три понятия, связанные с регулярными выражениями.
Во-первых, рано или поздно вы захотите найти какой-либо специальный символ, используемый в этом качестве и языком регулярных выражений. В таком случае необходимо задействовать префикс \ для экранирования этого специального символа. Например, для поиска символа правой скобки '(', используемого для группировки регулярных выражений, необходимо экранировать его следующим образом: \(. При этом символ '(' теряет особый смысл в регулярных выражениях.
Во-вторых, с помощью квадратных скобок [ ] можно описывать диапазоны конкретных символов. Например, регулярному выражению [0-9] соответствует любой из следующих символов: '0', '1', '2', ..., '9'. Еще один пример - регулярное выражение [a-e], которому соответствует любой из следующих символов: 'a', 'b', 'c', 'd', 'e'.
В-третьих, как мы обсуждали в посвященном предыдущему однострочнику разделе, регулярное выражение "скобки" (<pattern>) задает группу. Одна или несколько групп могут быть в любом регулярном выражении. При использовании функции re.findall() для включающего группы регулярного выражения в виде кортежа строковых значений будут возвращены только совпадающие группы, а не вся совпадающая строка. Например, регулярному выражению hello(world), вызванному для строки 'helloworld', соответствует вся строка, но возвращена будет только соответствующая ему группа world. С другой стороны, при использовании двух вложенных групп в регулярном выражении (hello(world)) результат функции re.findall() будет представлять собой кортеж всех подходящих групп ('helloworld', 'world'). Внимательно изучите следующий код, чтобы лучше разобраться с понятием вложенных групп:
string = 'helloworld' regex_1 = string = 'hello(world)' regex_2 = string = '(hello(world))' res_1 = re.findall(regex_1, string) res_2 = re.findall(regex_2, string) print(res_1) # ['world'] print(res_2) # [('helloworld', 'world')]
Теперь вы уже знаете все необходимое для понимания следующего фрагмента кода.
Напомним, что мы хотим посмотреть на все денежные суммы из заданного отчета компании. А именно, нам нужно решить следующую задачу: получить по заданной строке список всех вхождений сумм в долларах, возможно, с десятичными значениями. Например, следующих строк символов: $10, $10. и $10.00021. Как эффективно решить эту задачу с помощью одной строки кода? Взгляните на пример 5.4.
import re ## Данные report = ''' If you invested $1 in the year 1801, you would have $18087791.41 today. This is a 7.967% return on investment. But if you invested only $0.25 in 1801, you would end up with $4521947.8525. ''' ## Однострочник dollars = [x[0] for x in re.findall('(\$[0-9]+(\.[0-9]*)?)', report)] ## Результат print(dollars)
Попробуйте догадаться, каковы будут результаты выполнения этого фрагмента кода.
Отчет содержит четыре суммы в долларах в различных форматах. Необходимо разработать регулярное выражение, которому удовлетворяли бы они все. Мы разработали регулярное выражение (\$[0-9]+(\.[0-9]*)?), которому удовлетворяют следующие комбинации символов. Во-первых, знак доллара $ (мы его экранировали, поскольку он представляет собой специальный символ в регулярных выражениях). Во-вторых, число из произвольного количества цифр от 0 до 9 (но не менее одной). В-третьих, произвольное количество десятичных значений после (экранированного) символа точки (необязательного, как указывает регулярное выражение типа "один или ни одного" ?).
Более того, мы воспользовались списковым включением для извлечения только первого значения кортежа из всех трех найденных соответствий. Опять же, по умолчанию функция re.findall() возвращает список кортежей, по одному кортежу для каждого найденного соответствия и по одному значению кортежа для каждой группы в этом найденном соответствии:
[('$1', ''), ('$18087791.41', '.41'), ('$0.25', '.25'), ('$4521947.8525', '.8525')]
Нас интересует только общая группа - первое значение в кортеже. Остальные значения мы отфильтровываем с помощью спискового включения и получаем следующий результат:
## Результат print(dollars) # ['$1', '$18087791.41', '$0.25', '$4521947.8525']
Стоит опять отметить, насколько сложна и чревата ошибками была бы реализация даже простейшего синтаксического анализатора, не будь замечательных возможностей регулярных выражений!
На следующем шаге мы рассмотрим поиск небезопасных HTTP URL.