我正在尝试找到一个正则表达式,它匹配嵌套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"
+ "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表达式。这是为了确保它匹配仅我收到路径的密钥。 JSON字符串可以无限嵌套。
这是一个在线正则表达式测试程序,其示例如下: https://regex101.com/r/yNZ3wo/2
注意:
我知道这不是特定于python的,但我也很感激python的提示。我考虑构建自己的解析器,使用堆栈并计算{
和}
,但在此之前我想确保没有简单的正则表达式解决方案。
修改: 我知道json库,但这并不能解决我的情况,因为我在编辑器窗口内跟踪字符串表示中的目标坐标。我不是自己寻找值,我可以从相关的字典中访问它们。
答案 0 :(得分:1)
这很难。可能的解决方案是使用
(?<="a": )({(?>[^{}]|(?1))*})
({(?>[^{}]|(?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)))
或继续搜索下一级别(上面未显示)代码
基本上,您将以同样的方式从第1步再次继续inner
匹配(请参阅demo),例如:
(?<="c": )({(?>[^{}]|(?1))*})
这应该让你先行。
* 由于我们使用正则表达式递归,我们需要替代Python正则表达式包。
答案 1 :(得分:0)
感谢wp78de提供的答案,我意识到在这种情况下正则表达式不是正确的工具,至少不是我想要的工具。也许这对其他人有用,这就是为什么我在这里添加它。
所以,我编写了一个递归地解决问题的函数。
我利用了这样一个事实,即我知道哪个密钥必须在哪个级别匹配,因此在这种情况下它只会增加密钥索引(ind)。其他未按名称和级别匹配的键会触发异常。最后的if子句负责嵌套级别。
作为第一步,我将字符串转换为行列表(前面的空白被剥离):
d = \
{
"a": {
"b": {
"c": {
"d": "v1",
"key": "v2" # line 6
}
},
"x": {
"c": {
"d": "v11",
"key": "v20" # line 12
}
},
"c": {
"g": "v3",
"key": "v4" # line 17
},
"key": "v5" # line 19
}
}
ds = json.dumps(d, indent=4)
l = ds.split('\n')
ll = [x.lstrip() for x in l]
def findkey(l, t, lev=0, ind=0):
if ind == len(t):
return 1
else:
el = l[0]
try:
if el.startswith(t[ind]) and t.index(t[ind]) == lev:
ind += 1
except IndexError as e:
pass
if "{" in el:
lev += 1
if "}" in el:
lev -= 1
return 1 + findkey(l[1:], t, lev, ind)
以上只返回行号,但现在我可以用一个非常简单的正则表达式匹配我的目标:
idx = findkey(ll[1:], tup) - 1
s = re.compile(tup[-1] + ': (\s*(\[[^}]*?\]|".*?"|\d+\.*\d*))', re.DOTALL)
match = s.search(l[idx])
print("Value found at start index: {}, stop index: {}".format(match.start(1), match.end(2)))
输出:
Value found at start index: 19, stop index: 23
这是pyfiddle: