网站Bot框架开发:如何优化正则表达式多匹配逻辑?
问题描述
我正在为某网站开发首个Bot框架,目前编写了一个接收用户消息的类。网站消息分为两类:用户消息和系统消息,系统消息有20种左右(比如新订单、新评价、评价删除等)。
关键限制:所有消息在HTML层面完全一致,无法通过解析HTML区分消息类型,只能通过消息文本判断。
我现在给消息对象加了message_type属性,实现方式是写20个正则表达式,处理每条消息时循环遍历这些正则,找到能full_match()的项就返回对应的类型,否则判定为用户消息。想知道有没有优化这个流程的方法。
补充说明:只能获取消息文本,HTML层面无任何区分依据,用户也无法区分系统消息和普通消息(相关疑问请咨询网站开发者)。
系统消息示例:
- 新订单:
User X paid for order number 12321. - 新评价:
User X left a review for order number 12321. - 纠纷发起:
User X opened a dispute on order number 12321. - 纠纷关闭:
Administrator X has closed the dispute for order number 12321. - 部分退款:
Administrator X refunded some funds to customer Y for order number 12321.
当前实现代码:
from enum import Enum import re class MessageType(Enum): """所有系统消息类型的枚举""" NEW_ORDER = 1 NEW_REVIEW = 2 DISPUTE_OPENED = 3 DISPUTE_CLOSED = 4 PARTIAL_REFUND = 5 USER_MESSAGE = 0 # 消息类型对应的正则表达式映射 message_type_re = { MessageType.NEW_ORDER: re.compile(r"User [a-zA-Z0-9]+ paid for order number [0-9]{5}\.") , MessageType.NEW_REVIEW: re.compile(r"User [a-zA-Z0-9]+ left a review for order number [0-9]{5}\.") , MessageType.DISPUTE_OPENED: re.compile(r"User [a-zA-Z0-9]+ opened a dispute on order number [0-9]{5}\.") , MessageType.DISPUTE_CLOSED: re.compile(r"Administrator [a-zA-Z0-9]+ closed the dispute for order number [0-9]{5}\.") , MessageType.PARTIAL_REFUND: re.compile(r"Administrator [a-zA-Z0-9]+ refunded some funds to customer [a-zA-Z0-9]+ for order number [0-9]{5}\.") } class Message: """消息对象""" def __init__(self, text: str, *args): self.text: str = text """消息文本内容""" self.type = self.determine_message_type() """消息类型""" def determine_message_type(self) -> MessageType: """判断消息类型的方法""" for message_type in message_type_re: if message_type_re[message_type].fullmatch(self.text): return message_type return MessageType.USER_MESSAGE
优化方案
1. 合并正则表达式,一次匹配完成判断
把所有系统消息的正则合并成一个大正则,通过命名分组区分不同类型,只需一次匹配就能完成判断,避免循环遍历多个正则,效率最高。
示例代码:
from enum import Enum import re class MessageType(Enum): NEW_ORDER = 1 NEW_REVIEW = 2 DISPUTE_OPENED = 3 DISPUTE_CLOSED = 4 PARTIAL_REFUND = 5 USER_MESSAGE = 0 # 合并所有系统消息正则,用命名分组标记类型 combined_re = re.compile( r"(?P<new_order>User [a-zA-Z0-9]+ paid for order number [0-9]{5}\.)|" r"(?P<new_review>User [a-zA-Z0-9]+ left a review for order number [0-9]{5}\.)|" r"(?P<dispute_opened>User [a-zA-Z0-9]+ opened a dispute on order number [0-9]{5}\.)|" r"(?P<dispute_closed>Administrator [a-zA-Z0-9]+ closed the dispute for order number [0-9]{5}\.)|" r"(?P<partial_refund>Administrator [a-zA-Z0-9]+ refunded some funds to customer [a-zA-Z0-9]+ for order number [0-9]{5}\.)" ) # 分组名到MessageType的映射 group_to_type = { "new_order": MessageType.NEW_ORDER, "new_review": MessageType.NEW_REVIEW, "dispute_opened": MessageType.DISPUTE_OPENED, "dispute_closed": MessageType.DISPUTE_CLOSED, "partial_refund": MessageType.PARTIAL_REFUND } class Message: def __init__(self, text: str, *args): self.text = text self.type = self.determine_message_type() def determine_message_type(self) -> MessageType: match = combined_re.fullmatch(self.text) if match: # 遍历分组找到匹配的类型 for group_name, msg_type in group_to_type.items(): if match.group(group_name): return msg_type return MessageType.USER_MESSAGE
2. 按特征排序正则,减少平均匹配次数
如果某些系统消息出现频率更高,或者特征更独特(比如开头是Administrator和User的可以分组),可以把这类正则放在遍历顺序的前面,减少平均需要匹配的正则数量。
示例调整:
# 按开头特征分组排序,先处理Administrator开头的消息 message_type_re = [ (MessageType.DISPUTE_CLOSED, re.compile(r"Administrator [a-zA-Z0-9]+ closed the dispute for order number [0-9]{5}\.")), (MessageType.PARTIAL_REFUND, re.compile(r"Administrator [a-zA-Z0-9]+ refunded some funds to customer [a-zA-Z0-9]+ for order number [0-9]{5}\.")), (MessageType.NEW_ORDER, re.compile(r"User [a-zA-Z0-9]+ paid for order number [0-9]{5}\.")), (MessageType.NEW_REVIEW, re.compile(r"User [a-zA-Z0-9]+ left a review for order number [0-9]{5}\.")), (MessageType.DISPUTE_OPENED, re.compile(r"User [a-zA-Z0-9]+ opened a dispute on order number [0-9]{5}\.")) ] class Message: # 其他代码不变 def determine_message_type(self) -> MessageType: for message_type, pattern in message_type_re: if pattern.fullmatch(self.text): return message_type return MessageType.USER_MESSAGE
3. 保留预编译正则(已实现)
你当前代码中已经提前编译了所有正则,这是基础优化点,避免每次匹配都重新编译正则,建议继续保留。
总结
- 优先选择合并正则表达式的方案,一次匹配完成判断,效率最优;
- 如果合并后正则可读性下降,可以用按特征排序正则的方案,降低平均匹配次数;
- 20种类型的场景下,前两种方案完全够用,无需引入复杂的前缀树等实现。
内容的提问来源于stack exchange,提问作者Gygabrain
相关产品推荐
相关产品推荐

