【问题标题】:How to seperate dictionaries that are not comma separated?如何分隔非逗号分隔的字典?
【发布时间】:2020-02-13 12:57:10
【问题描述】:

我有一个文本文件,其中包含非逗号分隔的字典,格式如下:

{} {} {}

例子

{
    'header': 'sdf',
    'meta': {
        'searchId': {
            'searchId': 1234
        },
        'timestamp': 1234,
        'attachments': [
            'ABC'
        ],
        'xmlData': {
            'release': None,
            'version': None,
        }
} 
{
    'header': 'sdf',
        'timestamp': 14,
        'attachments': [
            'ABC'
        ],
        'xmlData': {
            'release': None,
            'version': None,
        }
} 

这些字典可能包含嵌套字典。我想阅读此文件并将其转换为字典列表,即格式为 [{},{},{}] 示例

[{
    'header': 'sdf',
    'meta': {
        'searchId': {
            'searchId': 1234
        },
        'timestamp': 1234,
        'attachments': [
            'ABC'
        ],
        'xmlData': {
            'release': None,
            'version': None,
        }
}, 
{
    'header': 'sdf',
        'timestamp': 14,
        'attachments': [
            'ABC'
        ],
        'xmlData': {
            'release': None,
            'version': None,
        }
}]

有人可以建议一种方法吗?

谢谢

【问题讨论】:

  • 是什么创建了这些数据?为什么不使用现有的序列化格式?
  • 基本上我得到了这种格式的数据。谁创建的并不重要,因为它不会被序列化
  • 没有真正的标准方法来反序列化这种格式的数据。如果您想以稳健的方式执行此操作,则需要编写自己的解析器。
  • 不,看,这就是问题所在。它序列化的(文件中没有“字典”,它是只有文本),有人做出了将字典的字符串表示形式转储到文件中的错误决定序列化就足够了。现在,您必须手动解析它。这可以通过使用适当的、受良好支持的序列化格式来避免(JSON 在这里很明显)。 最好的解决方案是修复源
  • 所有字符串都不会包含空格字符吗?

标签: python python-3.x list file dictionary


【解决方案1】:

我的另外两个答案假设数据文件中的字典位于不同的行上,因此每个字典都可以被解析为有效的 Python 语句。但是,如果不是这种情况,您可以使用 lib2to3 并修改 Grammar.txt 中的 Python 语法,以便简单语句(在语法文件中由 simple_stmt 表示)不必以换行符结尾:

from lib2to3 import fixer_base, refactor, pygram, pgen2
from io import StringIO
from functools import partialmethod
with open(pygram._GRAMMAR_FILE) as file:
    grammar = StringIO(''.join(line.replace(' NEWLINE', '') if line.startswith('simple_stmt:') else line for line in file))
pgen2.pgen.ParserGenerator.__init__ = partialmethod(pgen2.pgen.ParserGenerator.__init__, stream=grammar)
pygram.python_grammar = pgen2.pgen.generate_grammar()

并在顶层寻找atom 节点(其父节点没有父节点):

class ScrapeAtoms(fixer_base.BaseFix):
    PATTERN = "atom"

    def __init__(self, *args):
        super().__init__(*args)
        self.nodes = []

    def transform(self, node, results):
        if not node.parent.parent:
            self.nodes.append(node)
        return node

class Refactor(refactor.RefactoringTool):
    def get_fixers(self):
        self.scraper = ScrapeAtoms(None, None)
        return [self.scraper], []

    def get_result(self):
        return '[%s]\n' % ',\n'.join(str(node).rstrip() for node in self.scraper.nodes)

这样:

s = '''{'a': {1: 2}}{'b': 2}{
  'c': 3
}{'d': 4}'''
refactor = Refactor(None)
refactor.refactor_string(s, '')
print(refactor.get_result())

输出:

[{'a': {1: 2}},
{'b': 2},
{
  'c': 3
},
{'d': 4}]

演示:https://repl.it/@blhsing/CompleteStarchyFactorial

【讨论】:

    【解决方案2】:

    就像其他人在 cmets 中所说的那样。这不是json 数据。您只需将多个字符串表示的 dicts 连续漂亮地打印到文件中,而且您还缺少第一个中的右括号。

    所以我建议遍历文件并为每个字典构建一个字符串,然后您可以使用ast.literal_eval 将字符串解析为字典。像这样的:

    from ast import literal_eval
    
    current = ''
    data = []
    
    with open('filename.txt') as f:
        for line in f:
            if line.startswith('{'):
                current = line
            elif line.startswith('}'):
                data.append(literal_eval(current + line))
            else:
                current += line
    

    data 中的结果(使用pprint

    [{'header': 'sdf',
      'meta': {'attachments': ['ABC'],
               'searchId': {'searchId': 1234},
               'timestamp': 1234,
               'xmlData': {'release': None, 'version': None}}},
     {'attachments': ['ABC'],
      'header': 'sdf',
      'timestamp': 14,
      'xmlData': {'release': None, 'version': None}}]
    

    在此之后,您应该覆盖数据,并且永远不要再将其用作序列化。这就是为什么有这个库的原因。

    【讨论】:

    • 如果数据打印得很漂亮,它几乎可以解决问题,否则它会失败。我想这个问题唯一可行的是将数据转换成漂亮的打印格式然后继续,因为以原始形式处理数据需要使用解析器
    • @bhanugoyal 如果您可以控制数据的接收方式为什么要这样做?只需使用真正的序列化格式即可。
    • @juanpa.arrivillaga 我无法控制源数据。我所能做的就是以更易读的方式格式化它。
    【解决方案3】:

    由于文件中的每个 dict 都是有效的 Python 语句,因此更可靠的解决方案是使用 lib2to3 将文件解析为 Python 代码并提取语句节点,以便您可以将它们括在方括号中,分隔用逗号:

    from lib2to3 import fixer_base, refactor
    
    class ScrapeStatements(fixer_base.BaseFix):
        PATTERN = "simple_stmt"
    
        def __init__(self, *args):
            super().__init__(*args)
            self.nodes = []
    
        def transform(self, node, results):
            self.nodes.append(node)
            return node
    
    class Refactor(refactor.RefactoringTool):
        def get_fixers(self):
            self.scraper = ScrapeStatements(None, None)
            return [self.scraper], []
    
        def get_result(self):
            return '[%s]\n' % ',\n'.join(str(node).rstrip() for node in self.scraper.nodes)
    

    这样:

    s = '''{
        'header': 'sdf',
        'meta': {
            'searchId': {
                'searchId': 1234
            },
            'timestamp': 1234,
            'attachments': [
                'ABC'
            ],
            'xmlData': {
                'release': None,
                'version': None,
            }
        }
    }
    {
        'header': 'sdf',
            'timestamp': 14,
            'attachments': [
                'ABC'
            ],
            'xmlData': {
                'release': None,
                'version': None,
            }
    }
    '''
    
    refactor = Refactor(None)
    refactor.refactor_string(s, '')
    print(refactor.get_result())
    

    输出:

    [{
        'header': 'sdf',
        'meta': {
            'searchId': {
                'searchId': 1234
            },
            'timestamp': 1234,
            'attachments': [
                'ABC'
            ],
            'xmlData': {
                'release': None,
                'version': None,
            }
        }
    },
    {
        'header': 'sdf',
            'timestamp': 14,
            'attachments': [
                'ABC'
            ],
            'xmlData': {
                'release': None,
                'version': None,
            }
    }]
    

    【讨论】:

      【解决方案4】:

      如果文件中的所有 dicts 都在您的示例输入中的不同行上,那么每个 dict 本身就是一个有效的 Python 语句,因此您可以使用 ast.parse 将文件解析为抽象语法树,查找表达式节点(Expr 类型),并使用List 节点构建一个新的Expression 节点以容纳所有上述Expr 节点。然后可以将新的 Expression 节点编译和评估为实际的 Python 字典列表,以便在变量 s 中给定样本输入数据:

      import ast
      
      tree = ast.parse(s)
      exprs = [node.value for node in ast.walk(tree) if isinstance(node, ast.Expr)]
      new = ast.Expression(body=ast.List(elts=exprs, ctx=ast.Load()))
      ast.fix_missing_locations(new)
      lst = eval(compile(new, '', 'eval'))
      

      lst 会变成:

      [{'header': 'sdf',
        'meta': {'searchId': {'searchId': 1234},
                 'timestamp': 1234,
                 'attachments': ['ABC'],
                 'xmlData': {'release': None, 'version': None}}},
       {'header': 'sdf',
        'timestamp': 14,
        'attachments': ['ABC'],
        'xmlData': {'release': None, 'version': None}}]
      

      演示:https://repl.it/@blhsing/FocusedCylindricalTypes

      【讨论】:

        猜你喜欢
        • 2019-08-19
        • 1970-01-01
        • 1970-01-01
        • 2013-03-01
        • 2015-03-09
        • 2016-04-24
        • 1970-01-01
        • 1970-01-01
        • 2015-05-20
        相关资源
        最近更新 更多