Skip to main content
 首页 » 编程设计

python之在 Python 中使用正则表达式匹配 JSON 键

2024年12月31日26zhenyulu

我试图找到一个正则表达式来匹配嵌套 JSON 字符串表示的不同级别上的重复键。到目前为止,我所有的“解决方案”都遭受了灾难性的回溯。

该 JSON 字符串的示例如下所示:

d = {                
        "a": {       
            "b": { 
                "c": { 
                    "d": "v1",  
                    "key": "v2" 
                } 
            }, 
            "c": {   
                "g": "v3",      
                "key": "v4" 
            }, 
            "key": "v5"         
        } 
    } 

key 的值就是目标。我的应用程序确实具有指向该键的所有对象名称。使用这些名称,我可以使用 for 循环来构建我的最终正则表达式。所以基本上我需要把零件放在中间。

例子: 如果我得到 "a" 和 "key",我可以构造以下内容:"a"[^}]*"key"。这与我的字符串 d 中的第一个“key”相匹配,它的值为 v2。

不过,应该发生的是 "a" + "key" 匹配值为 v5 的键。当完整路径 "a" + "b" + "c" + "key 时,值为 v2 的键应该匹配" 进来了。这个例子中的最后一个例子是在 "a" + "c" + "key 时匹配键和值 v4 " 已给出。

所以最后一个的完整正则表达式看起来类似于:

"a"MATCH_EVERYTHING_IN_BETWEEN_REGEX"c"MATCH_EVERYTHING_IN_BETWEEN_REGEX"key":\s*(\[[^}]*?\]|".*?"|\d+\.*\d*)  

明确地说,我正在寻找可以作为连接器插入的 MATCH_EVERYTHING_IN_BETWEEN_REGEX 表达式。这是为了确保它仅匹配我收到的路径的 key 。 JSON 字符串可以无限嵌套。

这是一个带有示例的在线正则表达式测试器: https://regex101.com/r/yNZ3wo/2

注意: 我知道这不是特定于 python 的,但我也很感谢在此上下文中的 python 提示。我考虑过构建自己的解析器,使用堆栈并计算 { 和 } 但在此之前我想确保没有简单的正则表达式解决方案。

编辑: 我知道 json 库,但这并不能解决我的问题,因为我正在编辑器窗口内的字符串表示中跟踪我的目标坐标。我不是在寻找值本身,我可以从关联的字典中访问它们。

请您参考如下方法:

这很难。一个可能的解决方案是使用

  1. 用于匹配嵌套大括号的递归正则表达式*
    (?<="a": )({(?>[^{}]|(?1))*})
  2. 然后,继续使用垃圾桶方法在内部级别搜索键,即忽略整体匹配,只查看特定捕获组是否包含值
    (此处 $2,根据需要添加组):
    ({(?>[^{}]|(?1))*})|"key":\s*"([^"]*?)"

代码示例:

import regex as re 
 
test_str = ("{                   \n" 
    "  \"a\": {            \n" 
    "    \"b\": {          \n" 
    "      \"c\": {        \n" 
    "        \"d\": \"v1\",  \n" 
    "        \"key\": \"v2\" \n" 
    "      }             \n" 
    "    },              \n" 
    "    \"c\": {          \n" 
    "      \"g\": \"v3\",    \n" 
    "      \"key\": \"v4\"   \n" 
    "    },              \n" 
    "    \"key\": \"v5\"  \n" 
    "    }     \n" 
    "  }                 \n" 
    "}                   \n") 
 
regex = r"(?<=\"a\": )({(?>[^{}]|(?1))*})" 
innerRegex = r"({(?>[^{}]|(?1))*})|\"key\":\s*\"([^\"]*?)\"" 
 
matches = re.finditer(regex, test_str, re.DOTALL) 
 
for n, match in enumerate(matches): 
    n = n + 1     
    #print ("Match {n} was found at {start}-{end}: {match}".format(n = n, start = match.start(), end = match.end(), match = match.group())) 
    inner = match.group()[1:-1] 
 
    innerMatches = re.finditer(innerRegex, inner, re.DOTALL) 
    for m, innerMatch in enumerate(innerMatches): 
        #m = m + 1 
        if (innerMatch.groups()[1] is not None):           
            print ("Found at {start}-{end}: {group}".format(start = innerMatch.start(2), end = innerMatch.end(2), group = innerMatch.group(2))) 

或者继续搜索下一层(上面没有显示)代码。
基本上,您将从 inner 继续以相同的方式从步骤 1 开始再次匹配(参见 demo ),例如:

(?<="c": )({(?>[^{}]|(?1))*}) 

这应该让您抢先一步。

*由于我们使用正则表达式递归,我们需要替代的 Python 正则表达式包。