Как разделить словари, которые не разделены запятыми? - PullRequest
1 голос
/ 16 октября 2019

У меня есть текстовый файл, который содержит словари, не разделенные запятыми в следующем формате:

{} {} {}

Пример

{
    '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,
        }
}]

Может кто-нибудь предложить способ сделать это.

Спасибо

Ответы [ 4 ]

0 голосов
/ 17 октября 2019

Если все dict в файле находятся в отдельных строках, как и в вашем примере ввода, то каждый dict сам по себе является допустимым оператором Python, поэтому вы можете использовать ast.parse для синтаксического анализа файла в абстрактном синтаксическом дереве,найдите узлы выражений (типа Expr) и создайте новый узел Expression с узлом List для хранения всех вышеупомянутых 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

0 голосов
/ 16 октября 2019

Поскольку каждый 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,
        }
}]
0 голосов
/ 17 октября 2019

Мои два других ответа предполагают, что дикты в вашем файле данных находятся в отдельных строках, так что каждый диктовку можно проанализировать как допустимые операторы Python. Однако, если это не так, вы можете использовать lib2to3 и изменить грамматику Python в Grammar.txt , чтобы простой оператор (обозначенный 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

0 голосов
/ 16 октября 2019

Как и другие заявили в комментариях. Это не json данные. У вас просто есть несколько строковых представлений, которые довольно последовательно печатаются в файл, и в первой также отсутствует закрывающая скобка.

Поэтому я предлагаю пройтись по файлу и построить строку для каждого из этих сообщенийтогда вы можете использовать ast.literal_eval, чтобы разобрать строку в dict. Примерно так:

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}}]

После этого вы должны перезаписать данные, и никогдаиспользуйте это как сериализацию снова. Вот почему есть библиотеки для этого.

...