正则表达式:文本处理的瑞士军刀

🎯 前言:当文本遇上神奇的密码

想象一下,你是一个图书管理员,面对着一堆乱七八糟的书籍信息:

  • “联系电话:138-1234-5678”
  • “邮箱地址:zhang.san@gmail.com”
  • “身份证号:110101199001011234”
  • “QQ号码:12345678”

如果让你一个个手工整理,估计你会疯掉。但是,如果你掌握了正则表达式这把"瑞士军刀",这些看似繁琐的文本处理任务就会变得轻松愉快!

正则表达式(Regular Expression,简称 regex 或 regexp)就像是一种神奇的"文本搜索密码",它能够帮你在海量文本中精准定位你想要的内容。今天我们就来学习这门让文本处理变得如丝般顺滑的技艺!

📚 目录

  • 什么是正则表达式
  • 基础语法:学会写"文本密码"
  • Python中的正则表达式模块
  • 实战案例:从简单到复杂
  • 高级技巧:让你的正则更强大
  • 常见应用场景
  • 性能优化与最佳实践

🧠 什么是正则表达式

生活中的"模式匹配"

在解释正则表达式之前,我们先来看看生活中的例子。你有没有这样的经历:

  • 在一堆照片中找出所有包含"生日"的文件名
  • 在通讯录中找出所有138开头的手机号
  • 在邮件中找出所有的网址链接

这些其实都是"模式匹配"的过程,而正则表达式就是用来描述这些"模式"的一种特殊语言。

正则表达式的本质

正则表达式本质上就是一个字符串,但这个字符串有特殊的含义——它描述了一种文本模式。比如:

  • \d{3}-\d{4}-\d{4} 描述了"3位数字-4位数字-4位数字"的模式
  • \w+@\w+\.\w+ 描述了基本的邮箱格式模式

📖 基础语法:学会写"文本密码"

字符匹配:最基本的积木

import re# 直接匹配字符
text = "Hello World"
pattern = "Hello"
result = re.search(pattern, text)
print(result)  # <re.Match object; span=(0, 5), match='Hello'>

特殊字符:正则表达式的"魔法符号"

1. 点号(.):万能替身
# . 可以匹配任意单个字符(除了换行符)
pattern = "H.llo"
text1 = "Hello"  # 匹配
text2 = "Hallo"  # 匹配
text3 = "H@llo"  # 匹配
text4 = "Hllo"   # 不匹配for text in [text1, text2, text3, text4]:if re.search(pattern, text):print(f"'{text}' 匹配成功!")else:print(f"'{text}' 匹配失败!")
2. 星号(*):贪婪的小怪兽
# * 表示前面的字符可以出现0次或多次
pattern = "ab*c"
texts = ["ac", "abc", "abbc", "abbbc", "axc"]for text in texts:if re.search(pattern, text):print(f"'{text}' 匹配成功!")else:print(f"'{text}' 匹配失败!")
3. 加号(+):至少要有一个
# + 表示前面的字符至少出现1次
pattern = "ab+c"
texts = ["ac", "abc", "abbc", "abbbc"]for text in texts:if re.search(pattern, text):print(f"'{text}' 匹配成功!")else:print(f"'{text}' 匹配失败!")
4. 问号(?):可有可无的存在
# ? 表示前面的字符可以出现0次或1次
pattern = "colou?r"
texts = ["color", "colour", "colouur"]for text in texts:if re.search(pattern, text):print(f"'{text}' 匹配成功!")else:print(f"'{text}' 匹配失败!")

字符类:给字符分分类

1. 方括号:自定义字符集合
# [abc] 匹配a、b、c中的任意一个
pattern = "[abc]at"
texts = ["cat", "bat", "rat", "hat"]for text in texts:if re.search(pattern, text):print(f"'{text}' 匹配成功!")else:print(f"'{text}' 匹配失败!")
2. 范围表示:偷个懒的写法
# [a-z] 匹配任意小写字母
# [0-9] 匹配任意数字
# [A-Za-z0-9] 匹配任意字母或数字pattern = "[0-9]+"  # 匹配一个或多个数字
text = "我今年25岁,电话是138-1234-5678"
numbers = re.findall(pattern, text)
print(numbers)  # ['25', '138', '1234', '5678']
3. 预定义字符类:常用的简写
# \d 等价于 [0-9]
# \w 等价于 [a-zA-Z0-9_]
# \s 等价于 [ \t\n\r\f\v](空白字符)text = "用户名:zhang123,年龄:25岁"# 提取数字
numbers = re.findall(r'\d+', text)
print(f"数字:{numbers}")  # ['123', '25']# 提取单词字符
words = re.findall(r'\w+', text)
print(f"单词:{words}")  # ['zhang123', '25']

💻 Python中的正则表达式模块

re模块:Python的正则表达式工具箱

import re# 常用函数一览
text = "今天是2024年1月1日,明天是2024年1月2日"# 1. re.search() - 找到第一个匹配
result = re.search(r'\d{4}年\d{1,2}月\d{1,2}日', text)
if result:print(f"找到了:{result.group()}")  # 找到了:2024年1月1日# 2. re.findall() - 找到所有匹配
dates = re.findall(r'\d{4}年\d{1,2}月\d{1,2}日', text)
print(f"所有日期:{dates}")  # ['2024年1月1日', '2024年1月2日']# 3. re.sub() - 替换匹配的内容
new_text = re.sub(r'\d{4}年', '2025年', text)
print(f"替换后:{new_text}")  # 今天是2025年1月1日,明天是2025年1月2日# 4. re.split() - 按模式分割字符串
data = "苹果,香蕉;橘子:葡萄"
fruits = re.split(r'[,:;]', data)
print(f"水果列表:{fruits}")  # ['苹果', '香蕉', '橘子', '葡萄']

编译正则表达式:提高效率的小技巧

# 如果同一个正则表达式要使用多次,可以先编译
pattern = re.compile(r'\d{3}-\d{4}-\d{4}')phones = ["我的电话是138-1234-5678","客服热线:400-1234-5678","传真号码:010-8888-9999","这不是电话号码"
]for phone in phones:match = pattern.search(phone)if match:print(f"找到电话:{match.group()}")else:print("未找到电话号码")

🚀 实战案例:从简单到复杂

案例1:验证邮箱格式

def validate_email(email):"""验证邮箱格式是否正确"""# 基础版本basic_pattern = r'\w+@\w+\.\w+'# 进阶版本(更严格)advanced_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'if re.match(advanced_pattern, email):return Truereturn False# 测试不同的邮箱
emails = ["zhangsan@gmail.com",      # 正确"li.si@163.com",           # 正确"wang@company.com.cn",     # 正确"invalid-email",           # 错误"@gmail.com",              # 错误"test@",                   # 错误
]for email in emails:result = "✅ 有效" if validate_email(email) else "❌ 无效"print(f"{email}: {result}")

案例2:提取网页中的链接

def extract_links(html_content):"""从HTML内容中提取所有链接"""# 匹配 <a href="..."> 标签中的链接pattern = r'<a\s+href=["\'](.*?)["\']'links = re.findall(pattern, html_content, re.IGNORECASE)return links# 示例HTML内容
html = '''
<html>
<body><a href="https://www.python.org">Python官网</a><a href="mailto:admin@example.com">联系我们</a><a href="/about">关于我们</a><a HREF='https://github.com'>GitHub</a>
</body>
</html>
'''links = extract_links(html)
print("提取到的链接:")
for i, link in enumerate(links, 1):print(f"{i}. {link}")

案例3:解析日志文件

def parse_log_file(log_content):"""解析服务器日志文件"""# 典型的Apache日志格式# 127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234pattern = r'(\d+\.\d+\.\d+\.\d+).*?\[(.*?)\].*?"(\w+)\s+(.*?)\s+HTTP.*?"\s+(\d+)\s+(\d+)'matches = re.findall(pattern, log_content)logs = []for match in matches:ip, timestamp, method, url, status, size = matchlogs.append({'ip': ip,'timestamp': timestamp,'method': method,'url': url,'status': int(status),'size': int(size)})return logs# 示例日志内容
log_content = '''
127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234
192.168.1.1 - - [25/Dec/2023:10:05:00 +0000] "POST /login HTTP/1.1" 302 0
10.0.0.1 - - [25/Dec/2023:10:10:00 +0000] "GET /api/users HTTP/1.1" 404 567
'''logs = parse_log_file(log_content)
print("解析到的日志:")
for log in logs:print(f"IP: {log['ip']}, 状态: {log['status']}, URL: {log['url']}")

案例4:数据清洗与格式化

def clean_phone_numbers(text):"""清洗和格式化电话号码"""# 匹配各种格式的电话号码patterns = [r'(\d{3})-(\d{4})-(\d{4})',      # 138-1234-5678r'(\d{3})\s+(\d{4})\s+(\d{4})',  # 138 1234 5678r'(\d{3})\.(\d{4})\.(\d{4})',    # 138.1234.5678r'(\d{11})',                     # 13812345678]def format_phone(match):if len(match.groups()) == 3:return f"{match.group(1)}-{match.group(2)}-{match.group(3)}"else:# 对于11位连续数字,分割为3-4-4格式phone = match.group(1)return f"{phone[:3]}-{phone[3:7]}-{phone[7:]}"result = textfor pattern in patterns:result = re.sub(pattern, format_phone, result)return result# 测试数据
messy_text = '''
联系方式:
张三:138 1234 5678
李四:139.5678.9012
王五:13687654321
赵六:158-7890-1234
'''cleaned_text = clean_phone_numbers(messy_text)
print("清洗后的文本:")
print(cleaned_text)

🔧 高级技巧:让你的正则更强大

1. 分组捕获:抓住重要信息

# 使用括号创建捕获组
pattern = r'(\d{4})-(\d{2})-(\d{2})'
text = "今天是2024-01-15,明天是2024-01-16"for match in re.finditer(pattern, text):print(f"完整匹配:{match.group(0)}")print(f"年份:{match.group(1)}")print(f"月份:{match.group(2)}")print(f"日期:{match.group(3)}")print("---")

2. 命名捕获组:给组起个名字

# 使用 (?P<name>pattern) 创建命名组
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
text = "生日:1990-05-20"match = re.search(pattern, text)
if match:print(f"年份:{match.group('year')}")print(f"月份:{match.group('month')}")print(f"日期:{match.group('day')}")# 也可以用字典的方式访问date_dict = match.groupdict()print(f"日期字典:{date_dict}")

3. 非捕获组:只匹配不捕获

# 使用 (?:pattern) 创建非捕获组
pattern = r'(?:Mr|Mrs|Ms)\.\s+(\w+)'
text = "Mr. Smith 和 Mrs. Johnson 来了"matches = re.findall(pattern, text)
print(f"姓名:{matches}")  # ['Smith', 'Johnson']

4. 前瞻和后瞻:偷看前后的内容

# 正向前瞻 (?=pattern)
pattern = r'\d+(?=元)'  # 匹配后面跟着"元"的数字
text = "价格是100元,重量是50公斤"
prices = re.findall(pattern, text)
print(f"价格:{prices}")  # ['100']# 负向前瞻 (?!pattern)
pattern = r'\d+(?!元)'  # 匹配后面不跟着"元"的数字
weights = re.findall(pattern, text)
print(f"非价格数字:{weights}")  # ['50']

5. 贪婪与非贪婪匹配

text = '<div>内容1</div><div>内容2</div>'# 贪婪匹配(默认)
greedy_pattern = r'<div>.*</div>'
greedy_match = re.search(greedy_pattern, text)
print(f"贪婪匹配:{greedy_match.group()}")
# 结果:<div>内容1</div><div>内容2</div># 非贪婪匹配
non_greedy_pattern = r'<div>.*?</div>'
non_greedy_matches = re.findall(non_greedy_pattern, text)
print(f"非贪婪匹配:{non_greedy_matches}")
# 结果:['<div>内容1</div>', '<div>内容2</div>']

🎯 常见应用场景

1. 数据验证工具箱

class DataValidator:"""数据验证工具类"""@staticmethoddef validate_phone(phone):"""验证手机号码"""pattern = r'^1[3-9]\d{9}$'return bool(re.match(pattern, phone))@staticmethoddef validate_id_card(id_card):"""验证身份证号码"""pattern = r'^\d{17}[\dXx]$'return bool(re.match(pattern, id_card))@staticmethoddef validate_password(password):"""验证密码强度(至少8位,包含字母和数字)"""pattern = r'^(?=.*[a-zA-Z])(?=.*\d).{8,}$'return bool(re.match(pattern, password))@staticmethoddef validate_url(url):"""验证URL格式"""pattern = r'^https?://[^\s/$.?#].[^\s]*$'return bool(re.match(pattern, url))# 测试验证器
validator = DataValidator()test_data = {"手机号": "13812345678","身份证": "110101199001011234","密码": "password123","网址": "https://www.example.com"
}for data_type, value in test_data.items():if data_type == "手机号":result = validator.validate_phone(value)elif data_type == "身份证":result = validator.validate_id_card(value)elif data_type == "密码":result = validator.validate_password(value)elif data_type == "网址":result = validator.validate_url(value)status = "✅ 有效" if result else "❌ 无效"print(f"{data_type} {value}: {status}")

2. 文本信息提取器

class TextExtractor:"""文本信息提取器"""def __init__(self):self.patterns = {'email': r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b','phone': r'1[3-9]\d{9}','url': r'https?://[^\s]+','date': r'\d{4}[-/]\d{1,2}[-/]\d{1,2}','money': r'¥?\d+(?:\.\d{2})?','qq': r'[1-9]\d{4,11}','ip': r'\b(?:\d{1,3}\.){3}\d{1,3}\b'}def extract_all(self, text):"""提取文本中的所有信息"""results = {}for info_type, pattern in self.patterns.items():matches = re.findall(pattern, text)if matches:results[info_type] = matchesreturn results# 示例文本
sample_text = '''
个人信息:
姓名:张三
电话:13812345678
邮箱:zhangsan@gmail.com
QQ:123456789
生日:1990-05-20
网站:https://www.example.com
IP地址:192.168.1.1
余额:¥1234.56
'''extractor = TextExtractor()
extracted_info = extractor.extract_all(sample_text)print("提取到的信息:")
for info_type, values in extracted_info.items():print(f"{info_type}: {values}")

3. 日志分析工具

class LogAnalyzer:"""日志分析工具"""def __init__(self):# 定义不同类型的日志格式self.log_patterns = {'apache': r'(\d+\.\d+\.\d+\.\d+).*?\[(.*?)\].*?"(\w+)\s+(.*?)\s+HTTP.*?"\s+(\d+)\s+(\d+)','nginx': r'(\d+\.\d+\.\d+\.\d+).*?\[(.*?)\].*?"(\w+)\s+(.*?)\s+HTTP.*?"\s+(\d+)\s+(\d+)','error': r'\[(.*?)\]\s+\[(\w+)\]\s+(.*)','python': r'(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}),\d+\s+(\w+)\s+(.*)'}def analyze_access_log(self, log_content):"""分析访问日志"""pattern = self.log_patterns['apache']matches = re.findall(pattern, log_content)analysis = {'total_requests': len(matches),'unique_ips': set(),'status_codes': {},'popular_pages': {},'methods': {}}for match in matches:ip, timestamp, method, url, status, size = matchanalysis['unique_ips'].add(ip)# 统计状态码if status in analysis['status_codes']:analysis['status_codes'][status] += 1else:analysis['status_codes'][status] = 1# 统计热门页面if url in analysis['popular_pages']:analysis['popular_pages'][url] += 1else:analysis['popular_pages'][url] = 1# 统计请求方法if method in analysis['methods']:analysis['methods'][method] += 1else:analysis['methods'][method] = 1analysis['unique_ips'] = len(analysis['unique_ips'])return analysis# 示例日志分析
log_content = '''
127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234
192.168.1.1 - - [25/Dec/2023:10:05:00 +0000] "POST /login HTTP/1.1" 302 0
10.0.0.1 - - [25/Dec/2023:10:10:00 +0000] "GET /api/users HTTP/1.1" 404 567
127.0.0.1 - - [25/Dec/2023:10:15:00 +0000] "GET /about HTTP/1.1" 200 890
'''analyzer = LogAnalyzer()
analysis = analyzer.analyze_access_log(log_content)print("日志分析结果:")
print(f"总请求数:{analysis['total_requests']}")
print(f"唯一IP数:{analysis['unique_ips']}")
print(f"状态码分布:{analysis['status_codes']}")
print(f"热门页面:{analysis['popular_pages']}")
print(f"请求方法:{analysis['methods']}")

⚡ 性能优化与最佳实践

1. 编译正则表达式

import re
import time# 测试编译与不编译的性能差异
text = "这是一个测试文本,包含很多数字:12345,67890,13579,24680" * 1000def test_without_compile():"""不编译正则表达式"""start_time = time.time()for _ in range(1000):re.findall(r'\d+', text)end_time = time.time()return end_time - start_timedef test_with_compile():"""编译正则表达式"""pattern = re.compile(r'\d+')start_time = time.time()for _ in range(1000):pattern.findall(text)end_time = time.time()return end_time - start_timeprint(f"不编译耗时:{test_without_compile():.4f}秒")
print(f"编译后耗时:{test_with_compile():.4f}秒")

2. 避免回溯问题

# 不好的正则表达式(可能导致回溯)
bad_pattern = r'(a+)+b'# 好的正则表达式
good_pattern = r'a+b'# 测试文本(会导致回溯问题)
problematic_text = 'a' * 20# 建议:使用具体的量词而不是嵌套的量词

3. 使用正确的函数

text = "Hello World! This is a test."# 如果只需要知道是否匹配,使用 re.search()
if re.search(r'test', text):print("找到了 'test'")# 如果需要所有匹配,使用 re.findall()
words = re.findall(r'\w+', text)
print(f"所有单词:{words}")# 如果需要替换,使用 re.sub()
new_text = re.sub(r'test', 'example', text)
print(f"替换后:{new_text}")

🔧 常见问题与解决方案

问题1:特殊字符需要转义

# 错误:直接使用特殊字符
# pattern = r'$100'  # $ 是特殊字符,表示行尾# 正确:使用转义字符
pattern = r'\$100'  # 正确匹配 "$100"
text = "价格是$100"
result = re.search(pattern, text)
print(result.group() if result else "未找到")

问题2:中文字符处理

# 匹配中文字符
chinese_pattern = r'[\u4e00-\u9fff]+'
text = "Hello 世界!今天天气真好。"
chinese_words = re.findall(chinese_pattern, text)
print(f"中文内容:{chinese_words}")  # ['世界', '今天天气真好']

问题3:多行文本处理

multiline_text = '''第一行
第二行
第三行'''# 默认情况下,. 不匹配换行符
pattern1 = r'第一行.*第三行'
result1 = re.search(pattern1, multiline_text)
print(f"默认模式:{result1}")  # None# 使用 re.DOTALL 标志让 . 匹配换行符
pattern2 = r'第一行.*第三行'
result2 = re.search(pattern2, multiline_text, re.DOTALL)
print(f"DOTALL模式:{result2.group() if result2 else None}")

📖 扩展阅读

推荐资源

  • 在线正则表达式测试工具:regex101.com
  • Python re模块官方文档:docs.python.org/3/library/re.html
  • 正则表达式教程:regexone.com
  • 《正则表达式必知必会》:经典的正则表达式入门书籍

实用工具

# 正则表达式调试器
def regex_debugger(pattern, text):"""正则表达式调试器"""try:compiled = re.compile(pattern)matches = compiled.findall(text)print(f"模式:{pattern}")print(f"文本:{text}")print(f"匹配结果:{matches}")print(f"匹配数量:{len(matches)}")except re.error as e:print(f"正则表达式语法错误:{e}")# 使用示例
regex_debugger(r'\d+', '我有123个苹果和456个橘子')

🎬 下集预告

在下一篇文章《多线程与并发:让程序同时做多件事》中,我们将探索:

  • 如何让Python程序同时处理多个任务
  • 线程和进程的区别和使用场景
  • 异步编程的魅力
  • 并发编程的陷阱和最佳实践

想象一下,如果你的程序能够同时下载文件、处理数据、响应用户请求,那该多么高效!

📝 总结与思考题

核心要点回顾

  1. 正则表达式是处理文本的强大工具,就像文本处理的"瑞士军刀"
  2. 基础语法包括字符匹配、量词、字符类等
  3. Python的re模块提供了丰富的函数来使用正则表达式
  4. 实际应用包括数据验证、信息提取、日志分析等
  5. 性能优化通过编译正则表达式和选择合适的函数

实践作业

  1. 写一个函数,从一段文本中提取所有的IP地址
  2. 创建一个密码强度检查器,要求包含大小写字母、数字和特殊字符
  3. 解析一个CSV格式的字符串,提取每一列的数据
  4. 编写一个简单的模板引擎,用正则表达式替换模板中的变量

思考题

  1. 为什么说正则表达式是"写时简单,读时困难"?如何提高正则表达式的可读性?
  2. 在什么情况下应该使用正则表达式,什么情况下应该使用字符串的普通方法?
  3. 如何平衡正则表达式的功能性和性能?

记住:正则表达式虽然强大,但也要适度使用。正如一句名言所说:"如果你有一个问题,想用正则表达式解决,那么你就有两个问题了。"但是,如果你真正掌握了正则表达式,它就会成为你文本处理的得力助手!

🎉 恭喜你!你已经掌握了正则表达式这把文本处理的"瑞士军刀"。现在你可以轻松地在茫茫文本海洋中找到你需要的信息,就像拥有了一双火眼金睛!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91427.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91427.shtml
英文地址,请注明出处:http://en.pswp.cn/web/91427.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux下c语言访问mysql数据库

一、连接数据库基础1. 头文件与库文件连接 MySQL 需包含的头文件&#xff1a;#include <mysql/mysql.h> // 部分环境也可用 #include <mysql.h> 编译链接时&#xff0c;Linux 平台需指定库名&#xff1a;-lmysqlclient &#xff0c;用于链接 MySQL 客户端函数库。2…

6. 传输层协议 UDP

传输层负责数据能够从发送端传输接收端.1. 再谈端口号端口号(Port)标识了一个主机上进行通信的不同的应用程序在 TCP/IP 协议中, 用 "源 IP", "源端口号", "目的 IP", "目的端口号", "协议号" 这样一个五元组来标识一个通信…

vue 开发总结:从安装到第一个交互页面-与数据库API

vue 总结 1、安装vue&#xff1a; WinR 输入&#xff1a;cnpm install -g vue/cli 验证是否安装成功&#xff1a;vue --version 2、新建Vue工程 在对应文件夹下右击打开集成终端 输入 vue create query_system&#xff08;新建项目名字&#xff09;名称不能存在大写&#x…

运维笔记:HTTP 性能优化

一、HTTP 协议特性与性能瓶颈1.1 HTTP 协议发展历程HTTP 协议的演进直接影响着 Web 性能&#xff0c;各版本关键特性对比&#xff1a;协议版本发布时间核心特性性能优势局限性HTTP/1.01996 年无状态、短连接简单易实现每次请求需建立 TCP 连接HTTP/1.11999 年长连接、管道化减少…

ubuntu:运行gcfsd-admin守护进程需要认证,解决方法

这里有个锁子&#xff0c;每次进入都要输入密码&#xff0c;怎么解决&#xff1f; 重新挂载 /data 磁盘 sudo umount /data sudo ntfsfix /dev/sda1 sudo mount -o rw /dev/sda1 /data

1.DRF 环境安装与配置

文章目录一. Django Rest_Framework二、环境安装与配置2.1 安装 DRF2.2 创建Django项目2.3 添加 rest_framework 应用三、启动项目一. Django Rest_Framework 核心思想&#xff1a;大量缩减编写 api 接口的代码 Django REST framework 是一个建立在 Django 基础之上的 Web 应…

设计模式(十九)行为型:备忘录模式详解

设计模式&#xff08;十九&#xff09;行为型&#xff1a;备忘录模式详解备忘录模式&#xff08;Memento Pattern&#xff09;是 GoF 23 种设计模式中的行为型模式之一&#xff0c;其核心价值在于在不破坏封装性的前提下&#xff0c;捕获并外部化一个对象的内部状态&#xff0c…

Qt/C++开发监控GB28181系统/录像回放/切换播放进度立即跳转/支持8倍速播放/倍速和跳转进度无缝切换

一、前言说明 在国标监控系统中&#xff0c;录像回放过程中&#xff0c;需要切换播放进度&#xff0c;对比过很过国标系统&#xff0c;绝大部分尤其是网页版的监控系统&#xff0c;在切换进度过程中都会黑屏&#xff0c;这个体验就很不友好了&#xff0c;明明gb28181协议中就有…

【11】大恒相机SDK C++开发 ——原图像数据IFrameData内存中上下颠倒,怎么裁剪ROI 实时显示在pictureBox中

文章目录3 当内存中的 图像数据是垂直翻转的时候怎么截取ROI 并显示3.1 对ROI在原图中的位置做转换3.2 将ROI的最后一行当做开始位置&#xff0c;从底部向上复制数据3.3 完整代码3.4 图像数据在内存中上下颠倒的情况3.5 调用验证4 unsafe代码 解释及注意事项 看我另一篇文章5 C…

小架构step系列29:校验注解的组合

1 概述如果遇到某些属性需要多种校验&#xff0c;比如需要非空、符合某正则表达式、长度不能超过某值等&#xff0c;如果这种属性只有有限几个&#xff0c;那么手工把对应的校验注解都加上即可。但如果这种属性比较多&#xff0c;那么重复加这些校验注解&#xff0c;也是一种代…

网络基础19:OSPF多区域实验

一、拓扑结构1. 网络拓扑&#xff1a;骨干区域&#xff08;Area 0&#xff09;&#xff1a;连接核心设备&#xff08;AR1、AR2、AR3、AR4、AR5、AR6&#xff09;。非骨干区域&#xff1a;Area 1&#xff1a;AR5 ↔ AR9Area 2&#xff1a;AR5 ↔ AR10Area 3&#xff1a;AR6 ↔ A…

goland编写go语言导入自定义包出现: package xxx is not in GOROOT (/xxx/xxx) 的解决方案

问题 写了个自定义的包 calc.go&#xff0c;在路径 $GOPATH/go_project/src/demo_51_package/com/目录下&#xff0c;其中main.go 是main方法的入口代码 main.go 代码如下 package main import "demo_51_package/com" func main() {add : calc.Add(1, 2)println(add)…

HLS视频切片音频中断问题分析与解决方案

HLS视频切片音频中断问题分析与解决方案 问题背景 在使用FFmpeg进行HLS视频切片并通过hls.js前端播放时&#xff0c;开发者经常遇到一个典型问题&#xff1a;第一个视频切片播放正常且有声音&#xff0c;但后续切片却突然失去音频。这种现象在直播和点播场景中均有出现&#xf…

【Linux网络编程】网络层协议 - IP

目录 背景补充 协议头格式 IP报文的分片与组装 网段划分 网段划分是什么&#xff1f;为什么要进行网段划分&#xff1f; 怎么进行网段划分&#xff1f; 路由 路由表生成算法 背景补充 假设现在主机B要给主机C发送消息。在我们前面的学习中&#xff0c;一直都是将数据拷…

从“救火”到“先知”:润建曲尺运维大模型如何重构网络运维价值链

“7月18号&#xff0c;北京&#xff0c;晴&#xff0c;最高温度38摄氏度。”天气预报缓缓播报&#xff0c;商场、地铁、办公楼无不歌颂着威利斯开利的贡献&#xff0c;但这份凉爽的背后&#xff0c;离不开 “电” 的无声托举。5G毫秒级下载、丝滑的移动支付、智能电表、智能家居…

Element表格单元格类名动态设置

在 Element UI 的 el-table 组件中&#xff0c;cell-class-name 属性用于动态自定义表格单元格的 CSS 类名&#xff0c;通常用于根据数据条件设置样式。1. 基本用法在 el-table 上绑定 :cell-class-name 属性&#xff0c;值为一个函数。该函数接收一个对象参数&#xff0c;返回…

利用容器适配器实现stack和queue外加deque的介绍(STL)

文章目录前言什么是容器适配器&#xff1f;观察库中的源码那么该如何使用容器适配器呢&#xff1f;deque的简单介绍(了解)deque的原理介绍deque的优缺为什么选择deque作为stack和queue的底层默认容器&#xff1f;&#xff08;重点&#xff09;利用容器适配器实现我们自己的栈和…

【因子动物园巡礼】第12章:机器学习在因子投资中的应用(中文翻译)

【因子动物园巡礼】第12章&#xff1a;机器学习在因子投资中的应用&#xff08;中文翻译&#xff09;第12章 因子投资中的机器学习12.1 量化金融中的人工智能12.2 量化因子投资的AI化组件&#xff1a;解剖学视角12.2.1 数据源拓展与预处理12.2.2 因子研究12.2.3 因子模型12.2.4…

【Golang】用官方rate包构造简单IP限流器

文章目录使用 Go 实现基于 IP 地址的限流机制什么是 IP 限流&#xff1f;基于 rate.Limiter 实现 IP 限流1. 设计思路2. 代码实现3. 限流中间件4. 在 Gin 中使用中间件代码解释使用 Go 实现基于 IP 地址的限流机制 在高流量的服务中&#xff0c;限流是一个至关重要的环节。它不…

力扣 Pandas 挑战(6)---数据合并

本文围绕力扣的Pandas简单题集&#xff0c;解析如何用Pandas完成基础数据处理任务&#xff0c;适合Pandas初学者学习。题目1&#xff1a;1050. 合作过至少三次的演员和导演题目描述&#xff1a;ActorDirector 表&#xff1a;---------------------- | Column Name | Type | …