—— 正则表达式与文本魔法
# 分割与连接
csv_line = "张三,28,北京,Python开发"
fields = csv_line.split(",")
print(fields) # ['张三', '28', '北京', 'Python开发']
# join 性能远优于 + 连接
result = " | ".join(["A", "B", "C"])
print(result) # A | B | C
# partition - 分成三部分
url = "https://example.com/path"
protocol, _, rest = url.partition("://"
print(protocol, rest) # https example.com/path
# 多种对齐方式
text = "Python"
print(text.ljust(20, ".")) # Python..............
print(text.rjust(20, ".")) # ..............Python
print(text.center(20, "=")) # =======Python=======
print(text.zfill(20)) # 00000000000000Python
# strip 系列去除空白
dirty = " hello world "
print(dirty.strip()) # "hello world"
print(dirty.lstrip()) # "hello world "
print(dirty.rstrip()) # " hello world"
# translate - 高性能字符替换
import string
table = str.maketrans("0123456789", "零一二三四五六七八九")
print("2024年".translate(table)) # 二零二四年
| 语法 | 含义 | 示例 |
|---|---|---|
. | 任意字符(除换行) | a.c → abc, a1c |
\d | 数字 [0-9] | \d+ → 123 |
\w | 单词字符 [a-zA-Z0-9_] | \w+ → hello_1 |
\s | 空白字符 | a\sb → a b |
^ | 行首 | ^Hello |
$ | 行尾 | world$ |
* | 0次或多次 | ab*c → ac, abc |
+ | 1次或多次 | ab+c → abc, abbc |
? | 0次或1次 | ab?c → ac, abc |
{n,m} | n到m次 | \d{3,4} → 123, 1234 |
[...] | 字符集 | [aeiou] → 元音 |
(...) | 捕获组 | (\d+)-(\d+) |
| | 或 | cat|dog |
import re
# 1. search - 搜索第一个匹配
m = re.search(r'\d+', "订单号: ORD12345 已发货")
if m:
print(m.group()) # 12345
print(m.start()) # 8 起始位置
print(m.end()) # 13 结束位置
# 2. match - 从字符串开头匹配
m = re.match(r'Hello', "Hello World")
assert m is not None
# 3. findall - 找到所有匹配
numbers = re.findall(r'\d+', "a1b22c333")
print(numbers) # ['1', '22', '333']
# 4. finditer - 迭代所有匹配(获取位置信息)
for m in re.finditer(r'\d+', "a1b22c333"):
print(f"位置{m.start()}: {m.group()}")
# 5. sub - 替换
clean = re.sub(r'\s+', ' ', " 多余 空白 ")
print(clean) # " 多余 空白 "
# 6. split - 分割
parts = re.split(r'[,;|]', "a,b;c|d")
print(parts) # ['a', 'b', 'c', 'd']
# 编译正则(重复使用时提升性能)
pattern = re.compile(r'\d+')
print(pattern.findall("abc123def456")) # ['123', '456']
# 常用标志
# re.IGNORECASE - 忽略大小写
# re.MULTILINE - ^$ 匹配每行首尾
# re.DOTALL - . 匹配换行符
# re.VERBOSE - 允许注释和空白
import re
def validate_phone(phone: str) -> bool:
"""验证中国大陆手机号"""
pattern = r'^1[3-9]\d{9}$'
return bool(re.match(pattern, phone))
# 测试
assert validate_phone("13812345678") == True
assert validate_phone("12345678901") == False # 不以1[3-9]开头
assert validate_phone("1381234567") == False # 不足11位
print("✅ 手机号验证完成")
import re
def extract_emails(text: str) -> list[str]:
"""从文本中提取所有邮箱地址"""
pattern = r'[\w.+-]+@[\w-]+\.[\w.]+'
return re.findall(pattern, text)
text = """
联系:support@example.com 或 admin@test.org.cn
无效:not@email @missing.com
另外:user.name+tag@company.co.uk
"""
emails = extract_emails(text)
print(emails)
# ['support@example.com', 'admin@test.org.cn', 'user.name+tag@company.co.uk']
assert "support@example.com" in emails
print("✅ 邮箱提取完成")
import re
def parse_nginx_log(line: str) -> dict | None:
"""解析 Nginx 访问日志行"""
pattern = r'(?P\S+) \S+ \S+ \[(?P \
r(?P\S+) (?P\S+) \S+" ' \
r(?P\d+) (?P\d+)'
m = re.match(pattern, line)
if m:
return m.groupdict()
return None
log_line = '192.168.1.1 - - [10/Oct/2023:13:55:36 +0800] "GET /api/users HTTP/1.1" 200 1234'
result = parse_nginx_log(log_line)
assert result["ip"] == "192.168.1.1"
assert result["status"] == "200"
assert result["method"] == "GET"
print("✅ 日志解析完成")
import re
def strip_html(html: str) -> str:
"""去除HTML标签,保留文本内容"""
# 去除script和style内容
text = re.sub(r'', '', html, flags=re.DOTALL|re.IGNORECASE)
text = re.sub(r'', '', text, flags=re.DOTALL|re.IGNORECASE)
# 去除所有HTML标签
text = re.sub(r'<[^>]+>', '', text)
# 处理HTML实体
text = text.replace('&', '&').replace('<', '<').replace('>', '>')
# 压缩空白
text = re.sub(r'\s+', ' ', text).strip()
return text
html = '<h1>标题</h1><p>段落</p>'
result = strip_html(html)
assert '标题' in result and '段落' in result
print("✅ HTML清洗完成")
import re
def check_password_strength(password: str) -> dict:
"""检测密码强度"""
checks = {
"长度>=8": len(password) >= 8,
"包含大写": bool(re.search(r'[A-Z]', password)),
"包含小写": bool(re.search(r'[a-z]', password)),
"包含数字": bool(re.search(r'\d', password)),
"包含特殊字符": bool(re.search(r'[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]', password)),
}
score = sum(checks.values())
if score >= 5:
level = "强"
elif score >= 3:
level = "中"
else:
level = "弱"
return {"score": score, "level": level, "checks": checks}
result = check_password_strength("MyP@ss123")
assert result["level"] == "强"
assert result["score"] == 5
print("✅ 密码强度检测完成")
#!/usr/bin/env python3
"""第06课 字符串处理验证"""
import re
def test_string_ops():
"""字符串高级操作"""
assert " | ".join(["A","B","C"]) == "A | B | C"
assert "Python".ljust(10, ".") == "Python...."
assert "Python".center(10, "=") == "==Python=="
table = str.maketrans("0123456789", "零一二三四五六七八九")
assert "2024".translate(table) == "二零二四"
print("✅ 字符串高级操作测试通过")
def test_regex_phone():
"""手机号验证"""
pattern = r'^1[3-9]\d{9}$'
assert bool(re.match(pattern, "13812345678"))
assert not bool(re.match(pattern, "12345678901"))
print("✅ 手机号验证测试通过")
def test_regex_email():
"""邮箱提取"""
text = "联系:support@example.com 和 admin@test.org"
emails = re.findall(r'[\w.+-]+@[\w-]+\.[\w.]+', text)
assert "support@example.com" in emails
print("✅ 邮箱提取测试通过")
def test_regex_log():
"""日志解析"""
line = '192.168.1.1 - - [10/Oct/2023:13:55:36 +0800] "GET /api HTTP/1.1" 200 1234'
pattern = r'(?P\S+) \S+ \S+ \[(?P \
r(?P\S+) (?P\S+) \S+" (?P\d+) (?P\d+)'
m = re.match(pattern, line)
assert m.group("ip") == "192.168.1.1"
assert m.group("status") == "200"
print("✅ 日志解析测试通过")
def test_password_strength():
"""密码强度"""
pwd = "MyP@ss123"
checks = {
"len": len(pwd) >= 8,
"upper": bool(re.search(r'[A-Z]', pwd)),
"lower": bool(re.search(r'[a-z]', pwd)),
"digit": bool(re.search(r'\d', pwd)),
"special": bool(re.search(r'[!@#$%^&*]', pwd)),
}
assert all(checks.values())
print("✅ 密码强度测试通过")
if __name__ == "__main__":
test_string_ops()
test_regex_phone()
test_regex_email()
test_regex_log()
test_password_strength()
print("\n🎉 第06课全部验证通过!")
.*? 非贪婪,.* 贪婪