Регулярни изрази

„ Програмиране с Python“, ФМИ

24.03.2008г.

Задача

Искаме да се уверим, че подаденият ни телефонен номер е в следния формат: [02] 5–7 цифри

С други думи:


Например, валидни номера са 02555469, както и 987654.

Решение

# 15-минутно
# формат: [02] 5-7 цифри
def validate_phone_str(number):
    if '02' == number[:2]:
        return validate_phone_str(number[2:])
    if all([c.isdigit() for c in number]):
        return 5 <= len(number) <= 7
    return False
Няма ли по-естествено описание на формати за валидация? Е, има.
# шаблон, който се търси
def validate_phone_re(number):
	return bool(re.search(r'^(02)?\d{5,7}$', number))

Накратко за регулярните изрази

Задачката

Всичко е чудесно, но аз вече съм майстор на регулярните изрази.

За всички вас имаме следната задача:


Да се провери дали дадено число е просто чрез регулярен израз. Разрешени операции са:
  • re.search с подходящ шаблон.
  • Употребата на низа '1'.
  • Операторa *.

Големият тест

def testRun():
    samples = "01234567 523456 02789a012 027890012 023456 02023456".split()
    for number in samples:
        print "%s %s %s" % (number.ljust(10), 
                            str(validate_phone_str(number)).ljust(6), 
                            validate_phone_re(number))
>>> testRun()
01234567   False  False
523456     True   True
02789a012  False  False
027890012  True   True
023456     False  True
02023456   False  True
Хм. Я гледай ти... Къде сбъркахме?

Решение

# Грешното ни, 15-минутно решение
def validate_phone_str(number):
    if '02' == number[:2]:
        return validate_phone_str(number[2:])
    if all([c.isdigit() for c in number]):
        return 5 <= len(number) <= 7
    return False

# Едноминутното решение, работещо коректно.
def validate_phone_re(number):
	return bool(re.search(r'^(02)?\d{5,7}$', number))
Какво правим сега?
  • Да си оправим нашата validate_phone_str().
    Ами ако номерата имат вида (+359|00359|02)?[0-9]{5,7}?
  • Да се запознаем с новата материя.

Задаване на шаблон

Повторения (quantifiers)

Важат за непосредствено предхождащия ги символ/клас/група. Нека го означим с s.


matcher('o+', 'Goooooooogle')           # 'Goooooooogle'
matcher('[hH]o+', 'Hohohoho...')        # 'Hohohoho...'
# Хм. Не искахме точно това. По-скоро:
matcher('([hH]o)+', 'Hohohoho...')      # 'Hohohoho...'
matcher('([hH]o){2,3}', 'Hohohoho...')  # 'Hohohoho...'

По подразбиране — алчно търсене за съвпадение (greedy). Деактивира се с ? след квантора.

matcher('[hH]o+', 'Hoooooohohooo...')   # 'Hoooooohohooo...'
matcher('[hH]o+?', 'Hoooooohohooo...')  # 'Hoooooohohooo...'

Значения на специалните символи

Символни класове

Предефинирани класове

Групи

Групите са частите от даден шаблон, оградени в ( и ). Към тях можем да се обръщаме и от самия шаблон чрез специалните класове \1 — първата група, \2 — втората и така нататък. Няколко примера:


matcher(r'(\w+).*\1', 'Matches str if str repeats one of its words.');
'Matches str if str repeats one of its words.'

# Хм. Не точно. Нека опитаме пак:
matcher(r'(\b\w+\b).*\1', 'Matches str if str repeats one of its words.');
'Matches str if str repeats one of its words.'

Функции на модула re

Флагове

Кодът на matcher()

def matcher(regex, string):
    match = re.search(regex, string)
    if match is None: return string
    start, end = match.span()
    return string[:start] 
           + '«' + string[start:end] + '»' + 
           string[end:]

Въпроси?