Валидация email, телефонов, URL, дат, паролей и других форматов
Регулярные выражения широко используются для валидации данных: email, телефоны, URL, даты, пароли. В этой теме изучим практические паттерны для распространённых форматов.
import re
# Базовая валидация email
pattern = r'^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$'
def is_valid_email(email):
return bool(re.match(pattern, email))
is_valid_email('user@example.com') # True
is_valid_email('user.name@example.com') # True
is_valid_email('user@sub.example.co.uk') # True
is_valid_email('invalid') # False
is_valid_email('user@') # False# Без точек в начале/конце, без последовательных точек
pattern = r'^(?!.*\.\.)[\w.-]+[\w-]@[\w.-]+\.[a-zA-Z]{2,}$'
# (?!. *\.\.) — нет последовательных точек
# [\w.-]+[\w-] — не заканчивается на точку перед @# Минимум 8 символов, есть буква и цифра
pattern = r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$'# +7 и 10 цифр
pattern = r'^\+7\d{10}$'
is_valid_phone('+79991234567') # True
is_valid_phone('+70000000000') # True
is_valid_phone('89991234567') # False — начинается с 8# Разные форматы: +7 (999) 123-45-67, +7-999-123-45-67
pattern = r'^\+7[\s()\-0-9]{10,}$'
# Более строгий: конкретные форматы
pattern = r'^\+7\s?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}$'
is_valid('+7 (999) 123-45-67') # True
is_valid('+79991234567') # True
is_valid('+7-999-123-45-67') # True# E.164 формат: + и 7-15 цифр
pattern = r'^\+\d{7,15}$'
is_valid('+1234567890') # True
is_valid('+79991234567') # True
is_valid('+442071234567') # True# http/https домен и путь
pattern = r'^https?://[\w.-]+(?:/[\w./-]*)?$'
is_valid_url('https://example.com') # True
is_valid_url('http://example.com/path') # True
is_valid_url('ftp://example.com') # False — нет ftppattern = r'^https?://(?:[\w-]+\.)+[\w-]+(?::\d+)?(?:/[\w./-]*)?$'
# (?:[\w-]+\.)+ — один или более поддоменов
# [\w-]+ — домен верхнего уровня
# (?::\d+)? — опциональный порт
# (?:/[\w./-]*)? — опциональный путь
is_valid('https://www.example.com') # True
is_valid('https://sub.example.com:8080') # True
is_valid('https://example.com/path/page') # Truepattern = r'''
^https?:// # протокол
(?:[\w-]+\.)+[\w-]+ # домен
(?::\d+)? # порт
(?:/[\w./-]*)? # путь
(?:\?[^\s#]*)? # query string
(?:#[^\s]*)? # fragment
$
'''
re.compile(pattern, re.VERBOSE)pattern = r'^\d{4}-\d{2}-\d{2}$'
is_valid_date('2024-03-09') # True
is_valid_date('2024-3-9') # False — нужны ведущие нули# Год 1900-2099, месяц 01-12, день 01-31
pattern = r'^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$'
# (19|20)\d{2} — год 1900-2099
# (0[1-9]|1[0-2]) — месяц 01-12
# (0[1-9]|[12]\d|3[01]) — день 01-31
is_valid('2024-03-09') # True
is_valid('2024-13-01') # False — месяц 13
is_valid('2024-02-30') # True по regex, но неверная датаПримечание: Regex не может проверить корректность даты (30 февраля, 31 апреля). Для полной валидации используйте
datetime.
pattern = r'^(0[1-9]|[12]\d|3[01])\.(0[1-9]|1[0-2])\.(19|20)\d{2}$'
is_valid('09.03.2024') # True
is_valid('31.04.2024') # True по regex, но неверная дата# Минимум 8 символов
pattern = r'^.{8,}$'
is_valid('password') # True
is_valid('pass') # False# Минимум 8 символов, хотя бы одна буква и одна цифра
pattern = r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$'
# (?=.*[A-Za-z]) — есть буква
# (?=.*\d) — есть цифра
# [A-Za-z\d]{8,} — только буквы и цифры, минимум 8
is_valid('password1') # True
is_valid('password') # False — нет цифры
is_valid('12345678') # False — нет буквы# Минимум 8 символов, буква, цифра, спецсимвол
pattern = r'^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'
is_valid('Pass123!') # True
is_valid('Password1') # False — нет спецсимвола# 8-20 символов, заглавная, строчная, цифра, спецсимвол
pattern = r'''
^
(?=.*[a-z]) # хотя бы одна строчная
(?=.*[A-Z]) # хотя бы одна заглавная
(?=.*\d) # хотя бы одна цифра
(?=.*[@$!%*?&]) # хотя бы один спецсимвол
[A-Za-z\d@$!%*?&]{8,20}
$
'''
re.compile(pattern, re.VERBOSE)# 0-255.0-255.0-255.0-255
pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
# Более строгий: проверка диапазона 0-255
pattern = r'''
^
(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.
(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.
(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.
(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)
$
'''
# 25[0-5] — 250-255
# 2[0-4]\d — 200-249
# 1\d{2} — 100-199
# [1-9]?\d — 0-99
is_valid('192.168.1.1') # True
is_valid('255.255.255.255') # True
is_valid('256.1.1.1') # False# 8 групп по 4 hex-символа
pattern = r'^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$'
is_valid('2001:0db8:85a3:0000:0000:8a2e:0370:7334') # True# 16 цифр, возможно с пробелами или дефисами
pattern = r'^\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}$'
is_valid('1234567890123456') # True
is_valid('1234-5678-9012-3456') # True
is_valid('1234 5678 9012 3456') # True# Regex проверяет только формат, не алгоритм Luhn
pattern = r'^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})$'
# 4... — Visa
# 5[1-5]... — MasterCard
# 3[47]... — American Express# #RGB, #RRGGBB
pattern = r'^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$'
is_valid('#FFF') # True
is_valid('#FFFFFF') # True
is_valid('#ff5733') # True
is_valid('FFF') # False — нет #
is_valid('#GGGGGG') # False — G не hexclass FormValidator:
EMAIL_PATTERN = re.compile(r'^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$')
PHONE_PATTERN = re.compile(r'^\+7\d{10}$')
PASSWORD_PATTERN = re.compile(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$')
@staticmethod
def validate_email(email):
return bool(FormValidator.EMAIL_PATTERN.match(email))
@staticmethod
def validate_phone(phone):
return bool(FormValidator.PHONE_PATTERN.match(phone.replace(' ', '').replace('-', '')))
@staticmethod
def validate_password(password):
return bool(FormValidator.PASSWORD_PATTERN.match(password))def validate_password(password):
errors = []
if len(password) < 8:
errors.append('Минимум 8 символов')
if not re.search(r'[A-Za-z]', password):
errors.append('Хотя бы одна буква')
if not re.search(r'\d', password):
errors.append('Хотя бы одна цифра')
if not re.search(r'[@$!%*?&]', password):
errors.append('Хотя бы один спецсимвол')
return len(errors) == 0, errors
valid, errors = validate_password('pass')
# False, ['Минимум 8 символов', 'Хотя бы одна цифра', 'Хотя бы один спецсимвол']# Ошибка: не учитывает поддомены
pattern = r'^[\w]+@[\w]+\.[a-zA-Z]{2,}$'
# Несоответствует: user@sub.example.com
# Правильно: r'^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$'# Ошибка:соответствует что угодно
pattern = r'.+@.+\..+'
#соответствует: @., a@b.c
# Правильно: r'^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$'# Ошибка: regex не может проверить 30 февраля
pattern = r'^\d{4}-02-(30|31)$' # Всё равносоответствует
# Правильно: использовать datetime
from datetime import datetime
try:
datetime.strptime('2024-02-30', '%Y-%m-%d')
except ValueError:
print('Неверная дата')^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$^\+7\d{10}$^https?://[\w.-]+(?:/[\w./-]*)?$^\d{4}-\d{2}-\d{2}$(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\. для 0-255Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.