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

解析部分/不完整JSON时的灾难性回溯问题及解决方案咨询

处理TB级JSON时正则解析的灾难性回溯问题

我清楚用正则解析JSON并非最佳实践,但因需处理TB级规模的JSON数据,于是尝试实现了一个并行JSON读取器:通过截取随机数据块识别目标记录,再将块内首个匹配前的内容与前一块缓冲区合并、最后匹配后的内容与后一块缓冲区合并,再做分析。目前正则能正常识别首个及后续匹配,但在最后一个有效匹配后会陷入灾难性回溯。

相关正则表达式

{\s*
(?:
  \s*"negotiation_arrangement"\s*:\s*"[^"]+?"\s*,?\s*|
  \s*"name"\s*:\s*"[^"]+?",?\s*|
  \s*"billing_code_type"\s*:\s*"[^"]+?",?\s*|
  \s*"billing_code_type_version"\s*:\s*"[^"]+?",?\s*|
  \s*"billing_code"\s*:\s*"[^"]+?",?\s*|
  \s*"description"\s*:\s*"(?:\\.|[^"\\])*",?\s*|
  \s*"bundled_codes"\s*:\s*\[[^\]]*\]\s*,?|
  \s*"covered_service"\s*:\s*\[[^\]]*\]\s*,?|
  \s*"negotiated_rates"\s*:\s*(?:\[\s*
  (?:\s*\{\s*
  (?:
    \s*"provider_references"\s*:\s*\[[^"\[\]]+\]\s*,?\s*|
    (?:\s*"provider_groups"\s*:\s*\[\s*
      (?:{\s*
        (?:"npi"\s*:\s*\[[\d\,\s]*\]\s*,?\s*)|
        (?:"tin"\s*:\s*{\s*
          (?:"type"\s*:\s*"[^"]+"\s*,?\s*|
             "value"\s*:\s*"[^"]+"\s*,?\s*
          )+\s*\}\s*
        )\s*\}\s*,?\s*
      )+\s*],?
    )|
    (?:\s*"negotiated_prices"\s*:\s*\[\s*
      (?:\s*\{\s*
        (?:\s*"negotiated_type"\s*:\s*"[^"]*"\s*,? 
          |\s*"negotiated_rate"\s*:\s*[\d\.]*\s*,? 
          |\s*"expiration_date"\s*:\s*"[^"]*"\s*,? 
          |\s*"billing_class"\s*:\s*"[^"]*"\s*,? 
          |\s*"additional_information"\s*:\s*"[^"]*"\s*,?
          |\s*"service_code"\s*:\s*(?:\[[^\[\]]*\]|null),?
          |\s*"billing_code_modifier"\s*:\s*(?:\[[^\[\]]*\]|null),?
          ){0,7}
      \s*\},?)+
    )+\s*\]
  ){0,3}\s*\},?
)+
\s*\]\s*,?)
)+\s*}

引发问题的最小JSON示例

{
  "negotiated_rates": [
    {
      "provider_references": [
        532
      ],
      "negotiated_prices": [
        {
          "negotiated_rate": 3600
        }
    

解决方法

1. 优化正则表达式,消除灾难性回溯

灾难性回溯的根源是正则中存在大量嵌套的可选/重复结构(比如多层(?:...)+、宽松的{0,3}限定),匹配失败时引擎会尝试所有可能的回溯路径。可以通过以下方式修复:

  • 使用原子组:用(?>...)包裹重复的分支结构,让引擎一旦匹配成功就不再回溯,避免无效的路径尝试。
  • 收紧重复限定:把模糊的{0,3}这类限定改成更明确的范围,或者结合上下文减少分支可能性。
  • 明确闭合标记:确保正则能精准匹配到完整的JSON对象闭合括号,而非依赖模糊的\s*}。

针对你的正则,重点优化negotiated_rates内部的嵌套重复结构,比如将多层(?:...)+替换为原子组包裹的版本。

2. 给re.finditer添加超时限制

Python的re模块本身没有超时参数,但可以通过signal模块给匹配过程设置超时,避免无限循环:

import re
import signal

class TimeoutError(Exception):
    pass

def timeout_handler(signum, frame):
    raise TimeoutError("正则匹配超时")

def finditer_with_timeout(pattern, text, timeout=5):
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout)
    try:
        return list(re.finditer(pattern, text))
    finally:
        signal.alarm(0)

调用该函数时,若匹配超过设定时间会抛出TimeoutError,终止匹配过程。

3. 优化缓冲区截取逻辑

在截取数据块时,通过计数括号、引号的方式,确保块的结束位置落在非字符串、非数组/对象内部的位置,避免读取不完整的JSON结构。从根源上减少正则匹配失败的场景,也就不会触发灾难性回溯。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:30:09