Главная страница
Навигация по странице:

  • Выделение сумм в долларах из строковых значений

  • Поиск небезопасных HTTP URL

  • Проверка формата времени во вводимых пользователем данных, часть 2

  • однострочники пайтон. Однострочники Python лаконичный и содержательный код by Кристи. Однострочники


    Скачать 4.44 Mb.
    НазваниеОднострочники
    Анкороднострочники пайтон
    Дата22.04.2022
    Размер4.44 Mb.
    Формат файлаpdf
    Имя файлаОднострочники Python лаконичный и содержательный код by Кристи.pdf
    ТипКнига
    #490720
    страница16 из 21
    1   ...   13   14   15   16   17   18   19   20   21

    My Programming Links


    test your Python skills
    Learn recursion
    Great books from NoStarchPress
    Solve more Python puzzles

    192
    Глава 5. Регулярные выражения образом), за которым следует закрывающий тег. Таким образом мы находим все теги гиперссылок, содержащие интересующие нас строки символов.
    Обратите внимание, что данному регулярному выражению соответствуют и теги, в которых строка символов 'test'
    или 'puzzle'
    встречается вну- три самой ссылки. Учтите также, что мы используем только «нежадные» операторы «звездочка»
    '.*?'
    , чтобы всегда искать лишь минимальные со- ответствующие строки символов, вместо, например, очень длинной строки символов, заключенной во множество вложенных тегов.
    Результат нашего однострочника:
    ## Результат print(practice_tests)
    # [('test your Python skills', 'test'),
    # ('Solve more Python puzzles', 'puzzle')]
    Нашему регулярному выражению соответствуют две гиперссылки — резуль- тат однострочника представляет собой список из двух элементов. Однако каждый из этих элементов не просто строковое значение, а кортеж строковых значений, что отличается от результатов findall()
    , обсуждавшихся в пре- дыдущих фрагментах кода. В чем тут дело? Возвращаемый тип — список кортежей, по одному значению кортежа для каждой заключенной в скобки
    группы. Например, скобочная нотация служит для создания группы в регу- лярном выражении
    (test|puzzle)
    . При использовании скобочных групп в регулярном выражении функция re.findall()
    добавляет по одному значе- нию кортежа для каждой найденной группы. Значение кортежа представляет собой подстроку, соответствующую этой конкретной группе. Например, в нашем случае группе
    (test|puzzle)
    удовлетворяет подстрока 'puzzle'
    Посмотрим внимательнее на скобочные группы, чтобы лучше разобраться с этим понятием.
    Выделение сумм в долларах из строковых
    значений
    Этот однострочник демонстрирует еще одно применение регулярных вы- ражений на практике. В данном случае представьте себя финансовым ана- литиком. Ваша компания планирует приобрести другую, и вам поручили прочитать ее финансовые отчеты. Особенно вас интересуют все суммы в долларах. Конечно, вы можете просмотреть весь документ вручную, но это утомительная работа и вы не хотели бы тратить на нее самое продуктивное

    Выделение сумм в долларах из строковых значений
    193
    время дня. Поэтому вы решили написать небольшой сценарий на Python.
    Но как лучше всего это сделать?
    Общее описание
    К счастью, вы уже прочли мое руководство по регулярным выражениям, по- этому вместо того чтобы тратить огромное количество времени на написание собственного, очень большого и чреватого ошибками средства синтаксиче- ского разбора на Python, вы решили воспользоваться более аккуратной ре- ализацией на основе регулярных выражений — мудрое решение. Но прежде чем углубиться в решение поставленной задачи, обсудим еще три понятия, связанные с регулярными выражениями.
    Во-первых, рано или поздно вы захотите найти какой-либо специальный символ, используемый в этом качестве и языком регулярных выражений.
    В таком случае необходимо задействовать префикс
    \
    для экранирования это- го специального символа. Например, для поиска символа правой скобки '('
    , используемого для группировки регулярных выражений, необходимо экра- нировать его следующим образом:
    \(
    . При этом символ '('
    теряет особый смысл в регулярных выражениях.
    Во-вторых, с помощью квадратных скобок
    [
    ]
    можно описывать диапазоны конкретных символов. Например, регулярному выражению
    [0-9]
    соот- ветствует любой из следующих символов:
    '0'
    ,
    '1'
    ,
    '2'
    ,
    ,
    '9'
    . Еще один пример — регулярное выражение
    [a-e]
    , которому соответствует любой из следующих символов:
    'a'
    ,
    'b'
    ,
    'c'
    ,
    'd'
    ,
    'e'
    В-третьих, как мы обсуждали в посвященном предыдущему однострочнику разделе, регулярное выражение «скобки»
    (
    )
    задает группу. Одна или несколько групп могут быть в любом регулярном выражении. При ис- пользовании функции re.findall()
    для включающего группы регулярного выражения в виде кортежа строковых значений будут возвращены только совпадающие группы, а не вся совпадающая строка. Например, регуляр- ному выражению hello(world)
    , вызванному для строки 'helloworld'
    , со- ответствует вся строка, но возвращена будет только соответствующая ему группа world
    . С другой стороны, при использовании двух вложенных групп в регулярном выражении
    (hello(world))
    результат функции re.findall()
    будет представлять собой кортеж всех подходящих групп
    ('helloworld',
    'world')
    . Внимательно изучите следующий код, чтобы лучше разобраться с понятием вложенных групп:

    194
    Глава 5. Регулярные выражения string = 'helloworld'
    regex_1 = 'hello(world)'
    regex_2 = '(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.
    Листинг 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)
    Попробуйте догадаться, каковы будут результаты выполнения этого фраг- мента кода.

    Поиск небезопасных HTTP URL
    195
    Принцип работы
    Отчет содержит четыре суммы в долларах в различных форматах. Необ- ходимо разработать регулярное выражение, которому удовлетворяли бы они все. Мы разработали регулярное выражение
    (\$[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
    Данный однострочник демонстрирует, как решить одну из тех маленьких, но требующих много времени задач, с которыми так часто сталкиваются веб- разработчики. Допустим, вы ведете блог по программированию и только что перевели свой сайт с небезопасного протокола http на (более) безопасный

    196
    Глава 5. Регулярные выражения протокол https
    . Однако ссылки в ваших старых статьях по-прежнему ука- зывают на старые URL. Как же найти все эти ссылки на старые URL?
    Общее описание
    В предыдущем разделе вы научились с помощью нотации с квадратными скобками задавать произвольные диапазоны символов. Например, регуляр- ному выражению
    [0–9]
    соответствует любое число из одной цифры от 0 до 9.
    Однако потенциальные возможности нотации с квадратными скобками намного шире. Можно точно задавать символы, на соответствие (или несо- ответствие) которым необходимо проверить, путем указания в квадратных скобках произвольных сочетаний символов. Например, регулярному вы- ражению
    [0–3a–c]+
    соответствуют строки символов '01110'
    и '01c22a'
    , но не строка '443'
    или '00cd'
    . Можно также задавать фиксированный набор символов, которые не должны содержаться в найденном шаблоне, с помощью символа
    ^
    : регулярному выражению
    [^0–3a–c]+
    соответствуют строковые значения '4444d'
    и 'Python'
    и не соответствуют строки символов '001'
    и '01c22a'
    Код
    Входные данные здесь представляют собой многострочное строковое зна- чение, в котором нам нужно найти все вхождения допустимых URL, начи- нающихся с префикса http://
    . Однако рассматривать недопустимые URL без домена верхнего уровня (в найденном URL должна содержаться хотя бы одна
    ) не следует. Взгляните на листинг 5.5.
    Листинг 5.5. Однострочное решение для поиска правильных URL вида http://
    ## Зависимости import re
    ## Данные article = '''
    The algorithm has important practical applications http://blog.finxter.com/applications/
    in many basic data structures such as sets, trees,
    dictionaries, bags, bag trees, bag dictionaries,
    hash sets, https://blog.finxter.com/sets-in-python/
    hash tables, maps, and arrays. http://blog.finxter.com/
    http://not-a-valid-url

    Поиск небезопасных HTTP URL
    197
    http:/bla.ba.com http://bo.bo.bo.bo.bo.bo/
    http://bo.bo.bo.bo.bo.bo/333483--33343-/
    '''
    ## Однострочник stale_links = re.findall('http://[a-z0-9_\-.]+\.[a-z0-9_\-/]+', article)
    ## Результат print(stale_links)
    Опять же, попытайтесь догадаться, какой результат вернет этот код, прежде чем смотреть на приведенный ниже результат.
    Принцип работы
    В данном регулярном выражении мы анализируем многострочное строко- вое значение (возможно, старое сообщение из блога) в поисках всех URL, начинающихся со строкового префикса http://
    . На входе это регулярное выражение ожидает положительное количество символов в нижнем реги- стре, чисел, знаков подчеркивания, дефисов или точек (
    [a–z0–9_\–\.]+
    ).
    Обратите внимание, что мы экранируем дефис (
    \–
    ), поскольку в регуляр- ных выражениях с его помощью задаются диапазоны внутри квадратных скобок. Аналогично необходимо экранировать точку (
    \.
    ), поскольку мы хотим найти именно точку, а не произвольный символ. В результате полу- чаем следующее:
    ## Результаты print(stale_links)
    # ['http://blog.finxter.com/applications/',
    # 'http://blog.finxter.com/',
    # 'http://bo.bo.bo.bo.bo.bo/',
    # 'http://bo.bo.bo.bo.bo.bo/333483--33343-/']
    Похоже, необходимо перенести на более защищенный протокол HTTPS четыре правильных URL.
    Вы уже овладели основными возможностями регулярных выражений. Но действительно глубокого понимания можно достичь, лишь применяя их на практике и изучая большое количество примеров, и регулярные выражения не исключение. Рассмотрим еще несколько примеров того, как регулярные выражения могут облегчить жизнь программиста.

    198
    Глава 5. Регулярные выражения
    Проверка формата времени во вводимых
    пользователем данных, часть 1
    Научимся проверять правильность формата вводимых пользователем данных. Пусть вы пишете веб-приложение, вычисляющее медицинские показатели на основе длительности сна пользователей. Они вводят время отхода ко сну и время пробуждения. Пример времени в правильном форма- те —
    12:45
    , но из-за спама от веб-ботов большое количество испорченных данных приводит к излишней вычислительной нагрузке на сервер. Для решения этой проблемы вы хотите написать средство проверки формата времени, которое бы определяло, имеет ли смысл дальнейшая обработка ваших входных данных приложением, расположенным в прикладной ча- сти. Написание такого кода с помощью регулярных выражений занимает всего несколько минут.
    Общее описание
    В предыдущих нескольких разделах вы узнали про функции re.match()
    , re.search()
    и re.findall()
    . Они не единственные функции для работы с регулярными выражениями. В этом разделе мы воспользуемся функцией re.fullmatch(регулярное_выражение,
    строковое_значение)
    , проверяющей, соответствует ли регулярному выражению полное
    строковое_значение
    , как можно предположить из ее названия.
    Кроме того, мы воспользуемся синтаксисом
    <шаблон>{m,n}
    регулярных выражений, применяемым для поиска строки символов, включающей от m
    до n
    копий
    шаблон
    , но не более и не менее данного количества. Обратите внимание, что при этом производится попытка найти строку символов с максимальным количеством вхождений
    <шаблон>
    . Ниже представлен пример:
    import re print(re.findall('x{3,5}y', 'xy'))
    # []
    print(re.findall('x{3,5}y', 'xxxy'))
    # ['xxxy']
    print(re.findall('x{3,5}y', 'xxxxxy'))
    # ['xxxxxy']
    print(re.findall('x{3,5}y', 'xxxxxxy'))
    # ['xxxxxy']

    Проверка формата времени во вводимых пользователем данных, часть 1
    199
    При использовании нотации с фигурными скобками код не ищет соответ- ствий подстрок, включающих менее трех и более пяти символов 'x'
    Код
    Наша задача — написать функцию input_ok
    , принимающую строковый аргумент и проверяющий его на соответствие формату (времени)
    XX:XX
    , где
    X
    — число от 0 до 9 (листинг 5.6). Учтите, что пока мы не отбрасываем семантически неправильные форматы времени наподобие 12:86. Этой более сложной задачей мы займемся в следующем разделе.
    Листинг 5.6. Однострочное решение для проверки соответствия введенных пользователем данных общему формату времени XX:XX
    ## Зависимости import re
    ## Данные inputs = ['18:29', '23:55', '123', 'ab:de', '18:299', '99:99']
    ## Однострочник input_ok = lambda x: re.fullmatch('[0-9]{2}:[0-9]{2}', x) != None
    ## Результат for x in inputs:
    print(input_ok(x))
    Прежде чем продолжить чтение, попробуйте определить результаты шести вызовов функций в этом коде.
    Принцип работы
    Наши данные состоят из шести входных строковых значений, получаемых клиентской частью веб-приложения. Правильного ли они формата? Чтобы проверить, мы создаем функцию input_ok с помощью лямбда-выражения с одним входным аргументом x
    и булевым выходным значением. Далее с помощью функции fullmatch(регулярное_выражение,
    x)
    мы пытаемся установить соответствие входному аргументу x
    нашему регулярному вы- ражению для формата времени. Если это не удается, то результат принимает значение
    None
    , а булев выходной результат функции —
    False
    . В противном случае булев результат функции будет
    True

    200
    Глава 5. Регулярные выражения
    Само регулярное выражение очень простое:
    [0–9]{2}:[0–9]{2}
    . Ему соот- ветствуют два числа от 0 до 9, за которыми следует двоеточие
    :
    , за которым следуют еще два числа от 0 до 9. Таким образом, результат листинга 5.6 вы- глядит следующим образом:
    ## Результат for x in inputs:
    print(input_ok(x))
    '''
    True
    True
    False
    False
    False
    True
    '''
    Функция input_ok корректно находит правильно отформатированные зна- чения времени в inputs
    . В этом однострочнике показано, как часто встре- чающиеся на практике задачи, которые в противном случае потребовали бы больших усилий и многих строк кода, можно выполнить за несколько секунд с помощью правильного набора инструментов.
    Проверка формата времени во вводимых
    пользователем данных, часть 2
    В этом разделе мы еще больше углубимся в вопросы проверки формата времени во вводимых пользователями данных, чтобы решить проблему, упомянутую в предыдущем разделе: некорректные данные, введенные поль- зователем, например
    99:99
    не должны считаться допустимыми при поиске соответствий.
    Общее описание
    Одна из удобных стратегий решения задач — иерархическая. Для начала сведите задачу к простейшему виду и решите этот более простой ее вариант.
    Далее уточните решение, чтобы оно соответствовало конкретной (и более сложной) задаче, стоящей перед вами. В данном разделе мы существенно совершенствуем решение из предыдущего раздела: исключаем некорректные варианты вводимого времени наподобие
    99:99
    или
    28:66
    . Поэтому наша

    Проверка формата времени во вводимых пользователем данных, часть 2
    201
    задача становится более конкретной (и сложной), но мы можем повторно использовать отдельные части нашего старого решения.
    Код
    Наша задача — написать функцию input_ok
    , принимающую строковый аргумент и проверяющий его на соответствие формату (времени)
    XX:XX
    , где
    X
    — число от 0 до 9 (листинг 5.7). Кроме того, должен соблюдаться пра- вильный формат времени во входных данных — в 24-часовом диапазоне от 00:00 до 23:59.
    Листинг 5.7. Однострочное решение для проверки соблюдения общего формата времени XX:XX и 24-часового диапазона во введенных пользователем данных
    ## Зависимости import re
    ## Данные inputs = ['18:29', '23:55', '123', 'ab:de', '18:299', '99:99']
    ## Однострочник input_ok = lambda x: re.fullmatch('([01][0-9]|2[0-3]):[0-5][0-9]', x) != None
    ## Результат for x in inputs:
    print(input_ok(x))
    Этот код выводит шесть строк. Каких?
    Принцип работы
    Как уже упоминалось во введении к этому разделу, мы можем повторно использовать части предыдущего однострочника для упрощения реше- ния нашей задачи. Код остается тем же самым, меняется только регу- лярное выражение.
    ([01][0–9]|2[0–3]):[0–5][0–9]
    . Первая часть
    ([01]
    [0–9]|2[0–3])
    представляет собой группу, которой соответствуют все возможные часы в сутках. Оператор логического ИЛИ
    |
    здесь позволяет учесть часы от 00 до 19, с одной стороны, и от 20 до 23 — с другой. Второй части,
    [0–5][0–9]
    , соответствуют минуты от 00 до 59. В результате выво- дится следующее:
    ## Результат for x in inputs:
    print(input_ok(x))

    1   ...   13   14   15   16   17   18   19   20   21


    написать администратору сайта