python - 在 Python 中将 JSON 键与正则表达式匹配
问题描述
我正在尝试找到一个正则表达式,它匹配嵌套 JSON 字符串表示的不同级别上的重复键。到目前为止,我所有的“解决方案”都遭受了灾难性的回溯。
该 JSON 字符串的示例如下所示:
d = {
"a": {
"b": {
"c": {
"d": "v1",
"key": "v2"
}
},
"c": {
"g": "v3",
"key": "v4"
},
"key": "v5"
}
}
的值key
是目标。我的应用程序确实具有指向该键的所有对象名称。使用这些名称,我可以使用 for 循环来构造我的最终正则表达式。所以基本上我需要把零件放在中间。
示例:如果我得到"a"
并且"key"
我可以构造以下内容:"a"[^}]*"key"
. 这与我的字符串中的第一个“键”匹配d
,即值为 v2 的那个。
但是应该发生的是"a"
+"key"
匹配具有值 v5 的键。"a"
当完整路径++进来时,值 v2 的键应该匹配。本例中的最后一种情况是在给出 + "b"
+"c"
时匹配值v4的键。"key"
"a"
"c"
"key"
所以最后一个完整的正则表达式看起来类似于:
"a"MATCH_EVERYTHING_IN_BETWEEN_REGEX"c"MATCH_EVERYTHING_IN_BETWEEN_REGEX"key":\s*(\[[^}]*?\]|".*?"|\d+\.*\d*)
需要明确的是,我正在寻找可以作为连接器插入的 MATCH_EVERYTHING_IN_BETWEEN_REGEX 表达式。这是为了确保它只匹配我收到路径的密钥。JSON 字符串可以无限嵌套。
这是一个带有示例的在线正则表达式测试器: https ://regex101.com/r/yNZ3wo/2
注意:
我知道这不是特定于 python 的,但我也很感激在这种情况下的 python 提示。我考虑过构建自己的解析器,使用堆栈和计数{
,}
但在我想确保没有简单的正则表达式解决方案之前。
编辑: 我知道 json 库,但这并不能解决我的问题,因为我在编辑器窗口内的字符串表示中跟踪目标的坐标。我不是在寻找值本身,我可以从关联的字典中访问它们。
解决方案
这很难。一个可能的解决方案是使用
- 一个递归正则表达式 * 以匹配嵌套大括号
(?<="a": )({(?>[^{}]|(?1))*})
- 然后,使用垃圾箱方法继续搜索内部级别的键,即忽略整体匹配并仅查看特定捕获组是否包含值
(此处为 $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 步再次继续比赛(参见演示),例如:
(?<="c": )({(?>[^{}]|(?1))*})
这应该让您领先一步。
*由于我们使用正则表达式递归,我们需要替代 Python 正则表达式包。
推荐阅读
- encryption - TPM backed Certificate, alternative
- r - Cannot use if else with dplyr
- authentication - 如何在 ASP.NET Core 3.0 中获取 Windows 用户名
- php - IntelliJ Structural Search in PhpStorm to find 2 default statements within Switch block
- c# - ARCore augmented image - The Object you want to instantiate is null
- php - 如何修复无法与 ubuntu 一起使用的网站?
- python - 合并 2 个具有相同索引的数据帧
- r - 创建历史数据
- bootstrap-4 - Bootstrap 4:列之间的空间
- javascript - 在具有输入范围的元素上添加或删除类