Опубликован: 14.06.2015 | Доступ: свободный | Студентов: 4710 / 503 | Длительность: 21:48:00
Авторские права: Creative Commons Attribution 3.0
Самостоятельная работа 11:

Регулярные выражения

< Самостоятельная работа 1 || Самостоятельная работа 11: 12345

23.3. Сочетание поиска и извлечения

Пусть мы хотим найти числа во всех строках, которые начинаются с фрагмента "X-", например:

X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
  

Мы не хотим выделять числа в плавающем формате из любых строк – только из строк, имеющих вышеуказанный синтаксис. Для этого можно использовать следующее регулярное выражение:

^X-.*: [0-9.]+
  

Здесь указывается, что мы выбираем только строки, начинающиеся с фрагмента "X-", за которым следуют ноль или более произвольных символов ".*", затем двоеточие ":" и пробел. После пробела ищем один или более символов, каждый из которых является либо цифрой 0-9, либо точкой "[0-9.]+". Отметим, что точка внутри квадратных скобок обозначает обычный символ точки, а не произвольный символ (т.е. подстановка произвольного символа не действует внутри квадратных скобок).

Это очень компактное выражение, и оно выделяет из многообразия строк только те, которые нас интересуют:

import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
if re.search('^X\S*: [0-9.]+', line) :
print line
  

Выполнив программу, мы успешно отфильтруем только нужные нам строки.

X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
X-DSPAM-Confidence: 0.6178
X-DSPAM-Probability: 0.0000
  

Но теперь необходимо решить проблему извлечения чисел с помощью метода split. В принципе, и это совсем не сложно, но можно использовать другую возможность, предоставляемую регулярными выражениями – одновременный поиск и разбор.

Круглые скобки – это также специальные символы в регулярных выражениях. Когда мы ставим круглые скобки, они игнорируются при сопоставлении строки регулярному выражению, однако при использовании метода findall() скобки указывают, что, сопоставляя регулярное выражение целиком подстроке, мы извлекаем лишь ту часть подстроки, которая соответствует части регулярного выражения в скобках. Внесем следующее изменение в нашу программу:

import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('^X\S*: ([0-9.]+)', line)
if len(x) > 0 :
print x
  

Вместо вызова метода search() мы заключаем в круглые скобки ту часть регулярного выражения, которая представляет число в плавающем формате; тем самым мы указываем, что метод findall() должен возвращать лишь часть сопоставленной подстроки, представляющую число.

На выходе этой программы получаем:

['0.8475']
['0.0000']
['0.6178']
['0.0000']
['0.6961']
['0.0000']
..
  

Числа все еще содержатся в списке и должны быть преобразованы из строк в вещественные числа, но фактически мы уже воспользовались мощью регулярных выражений, чтобы найти и извлечь информацию, которая нас интересует.

Еще один пример применения этой техники: рассмотрим файл, который содержит некоторое количество строк в форме:

Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772

Пусть нам нужно извлечь все номера ревизий (целые числа в концах подобных строк). Можно использовать следующую программу:

import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('^Details:.*rev=([0-9.]+)', line)
if len(x) > 0:
print x
  

Здесь регулярное выражение означает, что мы ищем строки, начинающиеся со слова "Details:", за которым следует произвольное количество любых символов ".*", затем фрагмент "rev=" и далее одна или несколько цифр. Мы ищем строки, соответствующие всему выражению, но хотим извлечь из них только числа в концах строк, поэтому мы заключаем фрагмент " [0-9]+" в круглые скобки.

В результате работы программы получаем:

['39772']
['39771']
['39770']
['39769']
...
  

Помните, что фрагмент "[0-9]+" "жадный", он пытается выделить максимально большую подстроку из цифр перед ее извлечением. Это "жадное" поведение объясняет, почему выделяются все пять цифр каждого числа. Расширение происходит в обоих направлениях, пока не встретится либо не-цифра, либо начало или конец строки.

Теперь мы можем использовать регулярные выражения, чтобы переписать упражнение, рассмотренное ранее в этой книге, в котором нас интересовало время суток каждого письма в электронной почте. Мы искали строки вида:

From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008

Мы хотим извлечь час из каждой такой строки. Раньше мы делали это с помощью двух вызовов метода split. Сначала строка разделялась на слова, затем извлекалось пятое слово, которое в свою очередь разделялось с помощью двоеточия для извлечения интересовавших нас двух символов.

Хотя это и работало, мы получили в результате довольно ненадежный, хрупкий (brittle) код, предполагающий, что исходные строки правильно отформатированы. Если добавить проверку ошибок (или большой блок try/except), чтобы программа не отказывала при некорректно отформатированных входных данных, код раздулся бы до 10-15 строк и стал бы трудно читаемым.

Ту же задачу можно решить намного проще с помощью следующего регулярного выражения:

^From .* [0-9][0-9]:

Оно означает, что мы ищем строки, начинающиеся с фрагмента "From " (обратите внимание на пробел!), за которым следует произвольное количество любых символов ".*", затем пробел и дальше две цифры "[0-9][0-9]", после которых стоит символ двоеточия. Это точное определение строк, которые мы ищем.

Для извлечения часа с помощью метода findall() мы заключаем в круглые скобки часть выражения, соответствующую двум цифрам:

^From .* ([0-9][0-9]):

В результате получаем программу:

import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('^From .* ([0-9][0-9]):', line)
if len(x) > 0 : print x

На выходе она выдает

['09']
['18']
['16']
['15']
  
< Самостоятельная работа 1 || Самостоятельная работа 11: 12345
Ксения Шошина
Ксения Шошина

курс Программирование на Python

Илья Кизилов
Илья Кизилов

В лекции приводится программа для сортировки слов по их длинне. В коде ошибка. Я исправил так:

def sort_by_length(words):

words = words.split()

t = []

for word in words:

t.append((len(word), word))

t.sort(reverse=True)

res = []

for length, word in t:

res.append(word)

return res

print(sort_by_length(words))

 

Кто ещё как сделал?

 

Александра Пуличева
Александра Пуличева
Украина
Екатерина Ангел
Екатерина Ангел
Россия, г. Ноябрьск