Chapter 09 / 10

常用正则模式库

经过验证的正则表达式集合——邮箱、URL、日期、手机号、身份证、密码验证

WARNING以下模式都是实用近似,而非 100% 精确规范。生产环境中建议使用经过充分测试的库(如 Python 的 email-validatorphonenumbers),而不是手写正则。

邮箱地址

# 实用版(覆盖 95% 常见情况)
^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$

# Python 示例
EMAIL = re.compile(r'^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$')

def is_valid_email(email):
    return bool(EMAIL.fullmatch(email.strip()))

is_valid_email('user@example.com')   # True
is_valid_email('user+tag@sub.domain.org')  # True
is_valid_email('invalid@')          # False

URL

# HTTP/HTTPS URL
^https?://[^\s/$.?#].[^\s]*$

# 更完整的版本(解析各部分)
URL_PATTERN = re.compile(r"""
    ^
    (?P<scheme>https?|ftp)://          # 协议
    (?P<host>[a-zA-Z0-9.-]+)           # 主机名
    (?::(?P<port>\d{1,5}))?            # 端口(可选)
    (?P<path>/[^\s?#]*)?               # 路径(可选)
    (?:\?(?P<query>[^\s#]*))?          # 查询参数(可选)
    (?:\#(?P<fragment>\S*))?           # 锚点(可选)
    $
""", re.VERBOSE)

m = URL_PATTERN.match('https://example.com:8080/path?q=1#section')
m.groupdict()
# {'scheme': 'https', 'host': 'example.com', 'port': '8080',
#  'path': '/path', 'query': 'q=1', 'fragment': 'section'}

IP 地址

# IPv4(精确版,0-255 每段)
IPv4 = re.compile(r"""
    ^
    (?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)   # 0-255
    (?:
        \.
        (?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)
    ){3}
    $
""", re.VERBOSE)

IPv4.match('192.168.1.1')    # Match
IPv4.match('256.0.0.1')     # None(256 超出范围)

# IPv6(简化版)
IPv6 = re.compile(r'^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$')

日期与时间

# YYYY-MM-DD(不验证月日范围)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$

# HH:MM:SS(24小时制)
^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$

# ISO 8601 日期时间
^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$

中国手机号

# 1 开头,第 2 位 3-9,共 11 位
PHONE_CN = re.compile(r'^1[3-9]\d{9}$')

# 宽松版(允许 +86 前缀和分隔符)
PHONE_CN_LOOSE = re.compile(r'^(?:\+?86)?[-\s]?1[3-9]\d{9}$')

PHONE_CN.match('13800138000')   # Match
PHONE_CN.match('12345678901')   # None

中国居民身份证

# 18 位(含最后一位校验码 X 或数字)
ID_CARD = re.compile(r"""
    ^
    \d{6}                    # 地区码
    (19|20)\d{2}             # 年份(1900-2099)
    (0[1-9]|1[0-2])          # 月份
    (0[1-9]|[12]\d|3[01])    # 日期
    \d{3}                    # 顺序码
    [\dX]                    # 校验码
    $
""", re.VERBOSE)

# 注意:正则只验证格式,不验证校验码算法

密码强度

# 8位以上,含大写、小写、数字、特殊字符各至少一个
STRONG_PWD = re.compile(r"""
    ^
    (?=.*[A-Z])              # 至少一个大写字母
    (?=.*[a-z])              # 至少一个小写字母
    (?=.*\d)                 # 至少一个数字
    (?=.*[!@#$%^&*()_+\-=])  # 至少一个特殊字符
    .{8,}                    # 最短 8 位
    $
""", re.VERBOSE)

STRONG_PWD.match('Abc123!@')  # Match
STRONG_PWD.match('abc123!')   # None(无大写)

HTML / XML 片段

# 提取 HTML 标签名(不含属性)
re.findall(r'<(/?\w+)', html_text)

# 提取特定属性值
re.findall(r'href="([^"]*)"', html_text)

# 提取 class 名
re.findall(r'class="([^"]*)"', html_text)
INFO处理 HTML 时,正则只适合提取已知结构的简单属性。对于完整的 HTML 解析,请使用 BeautifulSoup(Python)或 DOMParser(JavaScript)等专业工具。

信用卡号(Luhn 算法前的格式验证)

# Visa: 4 开头,16 位
^4\d{15}$

# MasterCard: 51-55 或 2221-2720 开头
^(?:5[1-5]\d{14}|2(?:2[2-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12})$

# 通用信用卡(4 组 4 位数字,带/不带分隔符)
^\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}$

小结