You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用callable_iterator(re.finditer)导致Python冻结求助

问题:正则表达式迭代时程序冻结

现象描述

编写了文本行处理函数,使用re.finditer获取匹配迭代器时,打印迭代器对象正常,但将迭代器转为列表或循环迭代时,程序出现冻结。该问题仅在特定输入下触发:

  • 正常运行:tokenize_line('$color AS $length')
  • 触发冻结:tokenize_line('FALSE + $length IS GT 7 + $length IS 4')

核心代码片段

原始函数:

def tokenize_line(line: str, cmd = ''):
    matches = re.finditer(Patterns.SUPPORTED_TOKENS, line)
    tokens_found, not_found, start_idx = [], [], 0
    print(matches)
    for match in matches:
        pass
        # 剩余代码

触发冻结的操作:

# 转换为列表时冻结
matches = list(re.finditer(Patterns.SUPPORTED_TOKENS, line))

# 循环迭代时冻结
for match in matches:
   print(match)

使用的正则表达式模式

(°p\d+°|°a\d+°|°m\d+°)|((?<!\S)(?:!\'(?:\\.|[^\'\n\\])*\'|!\"(?:\\.|[^\n\"\\])*\")(?!\S))|((?:\'(?:\\.|[^\'\n\\])*\'|\"(?:\\.|[^\n\"\\])*\"))|((\{(.*)\}))|((?<!\S)([@$][\w]*(?:\.[\w]*)*)(?!\S))|((?<!\d)-?\d*\.?\d+)|(\*\*|[\+\-\*\(\)/%\^]|==|&&|\|\||!=|>=|<=|>|<<|~~|!~~|::|!::)|([\:/])|(\b(?:AS|AND|AT|:|BETWEEN|BY|FROM|IN|INTO|ON|OF|OR|THAN|TO|USING|WITH)\b)|(\b[a-zA-Z_][a-zA-Z0-9_]* *((?:[^;()\'\"\"]*|\"(?:[^\"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\'|\([^)]*\))*?;))|((\b(?:EMPTY|STRING|NUMBER|BOOL|ARRAY|MAP|TRUE|FALSE|NULL|UNKNOWN|DOTALL|IGNORECASE|MULTILINE|ARRAY_ARRAY|ARRAY_STRING|ARRAY_MAP|ARRAY_NUMBER|ARRAY_NULL|DOT|SPACE|NEWLINE|SEMICOLON|COLON|HASH|COMMA|TAB)\b)|(\b(?:IS NOT LT|IS NOT GT|IS NOT GEQ|IS NOT LEQ|IS NOT|IS LT|IS GT|IS GEQ|IS LEQ|IS|NOT IN|NOT|IN|HAS NOT|HAS|AND|OR)\b))

模式各部分说明

  • 自定义令牌:匹配°p\d+°、°a\d+°、°m\d+°格式的令牌
  • 带感叹号的引号字符串:匹配前后无空白字符的!'...'或!"..."格式字符串,支持转义
  • 普通引号字符串:匹配单/双引号包裹的字符串,支持转义
  • 花括号内容:匹配{...}内的所有内容
  • 变量:匹配前后无空白字符的@/$开头变量,支持嵌套属性(如$obj.attr)
  • 数字:匹配整数、浮点数及负数
  • 运算符:匹配各类数学/逻辑运算符(如**、&&、>=等)
  • 标点符号:匹配:和/
  • 保留关键字:匹配AS、AND、BETWEEN等语言关键字
  • 函数定义:匹配符合语法的函数定义结构(如func(...);)
  • 数据类型与逻辑运算符:匹配EMPTY、TRUE等类型关键字,以及IS NOT LT、HAS等复杂逻辑运算符

问题成因

核心是正则表达式的灾难性回溯,由两部分导致:

  1. 函数定义分支的贪婪嵌套匹配:函数定义分支中的[^;()\'\"\"]*与嵌套的\([^)]*\)组合,在遇到多运算符/空格的文本时,会尝试大量无效匹配路径,消耗CPU资源
  2. 逻辑运算符分支的重叠匹配:最后一个分支中存在大量重叠关键字(如AND同时出现在保留关键字和逻辑运算符分支),且长关键字(如IS NOT LT)与短关键字(如IS)并存,导致引擎反复尝试不同匹配组合,陷入无限回溯

解决方案

1. 优化正则表达式结构

调整匹配顺序:优先匹配长模式

将长逻辑运算符(如IS NOT LT)放在短关键字(如IS)之前,避免引擎先匹配短模式后回溯:

# 原逻辑运算符分支
(\b(?:IS NOT LT|IS NOT GT|IS NOT GEQ|IS NOT LEQ|IS NOT|IS LT|IS GT|IS GEQ|IS LEQ|IS|NOT IN|NOT|IN|HAS NOT|HAS|AND|OR)\b)
# 优化后(按长度从长到短排序)
(\b(?:IS NOT LT|IS NOT GT|IS NOT GEQ|IS NOT LEQ|IS NOT|IS LT|IS GT|IS GEQ|IS LEQ|NOT IN|HAS NOT|IS|NOT|IN|HAS|AND|OR)\b)

使用原子组禁止回溯

用(?>...)原子组包裹内部匹配逻辑,减少无效回溯。例如优化引号字符串匹配:

# 原引号字符串
(?:\'(?:\\.|[^\'\n\\])*\'|\"(?:\\.|[^\n\"\\])*\")
# 优化为原子组
(?:\'(?>\\.|[^\'\n\\])*\'|\"(?>\\.|[^\n\"\\])*\")

简化函数定义分支

将函数定义分支中的非贪婪匹配改为更严格的原子组匹配:

# 原函数定义分支
(\b[a-zA-Z_][a-zA-Z0-9_]* *((?:[^;()\'\"\"]*|\"(?:[^\"\\]|\\.)*\"|\'(?:[^\'\\]|\\.)*\'|\([^)]*\))*?;))
# 优化后
(\b[a-zA-Z_][a-zA-Z0-9_]* *+(?>[^;()\'\"\"\\]*(?:(?:\"(?>[^\"\\]+|\\.)*\"|\'(?>[^\'\\]+|\\.)*\'|\((?>[^()]+|\((?1)\))*\))[^;()\'\"\"\\]*)*?;)

移除冗余分支

删除重复的关键字(如AND、OR同时出现在两个分支),保留一处即可。

2. 预编译正则表达式

预编译正则表达式可避免重复编译开销,提升匹配效率:

import re

# 预编译优化后的正则
SUPPORTED_TOKENS_PATTERN = re.compile(OPTIMIZED_REGEX)

def tokenize_line(line: str, cmd = ''):
    if not line:
        return [], []
    matches = list(SUPPORTED_TOKENS_PATTERN.finditer(line))
    # 后续处理逻辑

3. 拆分正则表达式(可选)

若单一大正则仍有性能问题,可拆分为多个小正则按优先级依次匹配:

PATTERNS = [
    re.compile(r'°p\d+°|°a\d+°|°m\d+°'),
    re.compile(r'(?<!\S)(?:!\'(?>\\.|[^\'\n\\])*\'|!\"(?>\\.|[^\n\"\\])*\")(?!\S)'),
    re.compile(r'\'(?>\\.|[^\'\n\\])*\'|\"(?>\\.|[^\n\"\\])*\"'),
    # 其他模式按优先级排序
]

def tokenize_line(line: str, cmd = ''):
    tokens_found = []
    pos = 0
    while pos < len(line):
        matched = False
        for pattern in PATTERNS:
            match = pattern.match(line, pos)
            if match:
                tokens_found.append(match.group())
                pos = match.end()
                matched = True
                break
        if not matched:
            pos += 1
    return tokens_found, []

优化后的完整正则示例

(°p\d+°|°a\d+°|°m\d+°)|((?<!\S)(?:!\'(?>\\.|[^\'\n\\])*\'|!\"(?>\\.|[^\n\"\\])*\")(?!\S))|((?:\'(?>\\.|[^\'\n\\])*\'|\"(?>\\.|[^\n\"\\])*\"))|((\{(.*)\}))|((?<!\S)([@$][\w]*(?:\.[\w]*)*)(?!\S))|((?<!\d)-?\d*\.?\d+)|(\*\*|[\+\-\*\(\)/%\^]|==|&&|\|\||!=|>=|<=|>|<<|~~|!~~|::|!::)|([\:/])|(\b(?:AS|AND|AT|:|BETWEEN|BY|FROM|IN|INTO|ON|OF|OR|THAN|TO|USING|WITH)\b)|(\b[a-zA-Z_][a-zA-Z0-9_]* *+(?>[^;()\'\"\"\\]*(?:(?:\"(?>[^\"\\]+|\\.)*\"|\'(?>[^\'\\]+|\\.)*\'|\((?>[^()]+|\((?1)\))*\))[^;()\'\"\"\\]*)*?;)|\b(?:IS NOT LT|IS NOT GT|IS NOT GEQ|IS NOT LEQ|IS NOT|IS LT|IS GT|IS GEQ|IS LEQ|NOT IN|HAS NOT|IS|NOT|IN|HAS)\b

内容的提问来源于stack exchange,提问作者amarodev05

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:54:52