从字符串中提取字典

5
我正在调用一个返回包含字典的字符串的函数。如何提取这个字典,同时注意到第一行和最后一行可能包含 '{' 和 '}' 符号。
This is a {testing string} example
This {is} a testing {string} example
{"website": "stackoverflow",
"type": "question",
"date": "10-09-2020"
}
This is a {testing string} example
This {is} a testing {string} example

我需要将这个值提取为字典变量。
{"website": "stackoverflow",
"type": "question",
"date": "10-09-2020"
}

2
该函数是否需要更新以返回一个适当的字典?(本质上,解决数据问题的根源,而不是编写额外的代码来处理它。) - S3DEV
很遗憾,我无法控制该函数的输出。我正在使用子进程调用一个shell命令,并且我期望输出始终以那种格式呈现。 - Mehdi Khlifi
所以我需要提取字典内部,以便我可以创建适当的输出,即删除某些键/值。 - Mehdi Khlifi
4
因为您的输入可能包含带有 {} 字符的内容,所以您需要找到每个成对的字符,并检查它是否包含有效的字典(同时希望没有东西恰好看起来像是一个有效的字典)。 - martineau
@MehdiKhlifi 一个真实的输入文件是什么样子?字典部分总是以完全相同的方式编写吗?它是否包含嵌入式字典和/或列表?字典中的字符串值是否包含括号和大括号? - ekhumoro
显示剩余2条评论
1个回答

5
更新的答案
从@martineau和@ekhumoro的评论中得到启发,下面编辑的代码包含一个函数,该函数搜索字符串并提取有效的所有字典。这是比我之前的答案更健壮的方法,因为现实世界中字典的内容可能会有所变化,而这个逻辑(希望)能够考虑到这一点。
示例代码:
import json
import re

def extract_dict(s) -> list:
    """Extract all valid dicts from a string.
    
    Args:
        s (str): A string possibly containing dicts.
    
    Returns:
        A list containing all valid dicts.
    
    """
    results = []
    s_ = ' '.join(s.split('\n')).strip()
    exp = re.compile(r'(\{.*?\})')
    for i in exp.findall(s_):
        try:
            results.append(json.loads(i))        
        except json.JSONDecodeError:
            pass    
    return results

测试字符串:

原始字符串已更新,添加了多个dict、一个数字值作为最后一个字段和一个list值。

s = """
This is a {testing string} example
This {is} a testing {string} example
{"website": "stackoverflow",
"type": "question",
"date": 5
}
{"website": "stackoverflow",
"type": "question",
"date": "2020-09-11"
}
{"website": "stackoverflow",
"type": "question",
"dates": ["2020-09-11", "2020-09-12"]
}
This is a {testing string} example
This {is} a testing {string} example
"""

输出:

正如原贴所述,通常在字符串中只有一个 dict,因此可以使用 results[0] 进行访问。

>>> results = extract_dict(s)

[{'website': 'stackoverflow', 'type': 'question', 'date': 5},
 {'website': 'stackoverflow', 'type': 'question', 'date': '2020-09-11'},
 {'website': 'stackoverflow', 'type': 'question', 'dates': ['2020-09-11', '2020-09-12']}]

原始答案:


忽略此部分。虽然代码可以工作,但它仅适用于特定的请求,对于其他用途而言并不健壮。

此示例使用正则表达式识别字典开始 {" 和字典结束 "},提取中间内容,然后将字符串转换为合适的 dict。由于换行符会干扰并复杂化正则表达式,因此我只是将字符串展开以便开始。

根据 @jizhihaoSAMA 的评论,我已更新为使用 json.loads 将字符串转换为 dict,因为这更加清晰。如果您不想要额外的导入,则也可以使用 eval,但这种方法不被推荐。

示例代码:

import json
import re

s = """
This is a {testing string} example
This {is} a testing {string} example
{"website": "stackoverflow",
"type": "question",
"date": "10-09-2020"
}
This is a {testing string} example
This {is} a testing {string} example
"""

s_ = ' '.join(s.split('\n')).strip()
d = json.loads(re.findall(r'(\{\".*\"\s?\})', s_)[0])

>>> d
>>> d['website']

输出:

{"website": "stackoverflow", "type": "question", "date": "10-09-2020"}

'stackoverflow'

2
不建议使用eval(),尝试使用更安全的函数,如ast.literal_eval()json.loads - jizhihaoSAMA
很可能OP的真实输入不会像测试样例那么简单,因此这种方法在实践中几乎不可能奏效。 - ekhumoro
@ekhumoro - 在现实世界中,这是一个公正的假设,但我认为这个评论是不公平的 - 因为这种观点几乎可以适用于 SO 上的所有答案,并使它们失效。这个解决方案满足了 OP 的要求并提供了示例。除此之外的任何事情都超出了范围。 - S3DEV
1
这完全不是真的。通常情况下,可以从测试用例中概括出大多数实际可能性。如果您的解决方案可以被输入的微小更改所否定,那么它并没有以有用的方式满足要求。(例如,如果字典中的最后一个值恰好是数字而不是字符串,则您的解决方案将会出现问题)。 - ekhumoro
@ekhumoro - 看来你说得有道理,我已经更新了答案。感谢你的拓展思路。 - S3DEV
在删除第3行的-> list之后,完美地运行了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接