Разобрать текст в блоки - PullRequest
0 голосов
/ 28 мая 2018

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

start
id=1
date=21.05.2018
summ=500
end

start
id=7
date=23.05.2018
summ=500
owner=guest
end

, и мне нужно проанализировать его в списке словарей (str: str (даже если это тип int или дата: преобразовать его встрока)).то есть разделить его на блок с помощью start end, а после этого разделить его на символ =.Количество строк между start end может быть разным.D Но я не могу этого понять.Я пробовал что-то вроде этого:

d ={}
arr = []
ind = 0
for line in plines:
    ind = ind + 1
    if 'startpayment' in line:
        print('ind = ' + str(ind))
        for i in range(ind, len(plines)):
            print(i)
            key, value = plines[i].strip().split('=')
            if type(value) == 'str':
                d[key] = str(value)
            elif type(value) == 'int':
                 d[key] = int(value)
            arr.append(d)
            if 'endpayment' in line:
                break

Может ли кто-нибудь мне помочь?Спасибо

Ответы [ 5 ]

0 голосов
/ 28 мая 2018

Вы можете просто проанализировать текстовый файл, если у вас есть какой-то контекст: запустить новый словарь в каждой строке start и добавить его в список в каждой строке end .

Код может быть:

def parse(fd):
    """Parse a file, fd is expected to be a file object"""
    resul = []     # the list of dictionaries to return
    d = None       # an individual dict initialized to None
    linenum = 0
    for line in fd:
        line = line.strip()
        linenum += 1
        if line.startswith('end'):
            if d is not None:
                resul.append(d)
                d = None
        elif line.startswith('start'):
            d = {}
        elif len(line) != 0:
            key, val = line.split('=', 1)
            d[key] = val
    return resul

Синтаксические ошибки в файле (отсутствующие начальная или конечная строки, другие неправильные строки) здесь не обрабатываются:

  • отсутствующий конецстрока приведет к тому, что предыдущие ключи = строки val будут отброшены
  • пропущенная строка запуска вызовет исключение, поскольку None не является допустимым для следующего ключа = строка val
  • anдругая неправильная строка (без знака =) должна вызвать исключение ValueError: недостаточно значений для распаковки (ожидается 2, получено 1)
0 голосов
/ 28 мая 2018

Вы также можете попробовать что-то вроде этого:

from itertools import takewhile

with open('data.txt') as in_file:
    items = [line.strip() for line in in_file.read().split()]
    # ['start', 'id=1', 'date=21.05.2018', 'summ=500', 'end', 'start', 'id=7', 'date=23.05.2018', 'summ=500', 'owner=guest']

    pos = [i for i, item in enumerate(items) if item == 'start']
    # [0, 5]

    blocks = [list(takewhile(lambda x: x != 'end', items[i+1:])) for i in pos]
    # [['id=1', 'date=21.05.2018', 'summ=500'], ['id=7', 'date=23.05.2018', 'summ=500', 'owner=guest']]

    print([dict(x.split('=') for x in block) for block in blocks])

Какие выходы:

[{'id': '1', 'date': '21.05.2018', 'summ': '500'}, {'id': '7', 'date': '23.05.2018', 'summ': '500', 'owner': 'guest'}]
0 голосов
/ 28 мая 2018

Вы можете создать простой парсер с рекурсией, который пытается найти данные между start и end блоками:

import re
class Parser:
  def __init__(self, source:str):
    self.source = iter(filter(None, source.split('\n')))
    self.results = []
    self.parse()
  @staticmethod
  def to_dict(between_blocks):
    return dict(re.split('\s*\=\s*', i) for i in between_blocks)
  def parse(self):
    _line = next(self.source, None)
    if _line is not None:
      if _line == 'start':
        scope = []
        while True:
         _temp = next(self.source, None)
         if _temp is None:
           raise Exception("Missing 'end' tag")
         if _temp != 'end':
           scope.append(_temp)
         else:
           break
        self.results.append(Parser.to_dict(filter(None, scope)))
      self.parse()
   def __repr__(self):
      return f'{Parsed}({self.results})'

print(Parser(open('filename.txt').read())).results)

Вывод:

[{'id': '1', 'date': '21.05.2018', 'summ': '500'}, {'id': '7', 'date': '23.05.2018', 'summ': '500', 'owner': 'guest'}]

Тесты:

tests = [[
"""
start
id=1
date=21.05.2018
summ=500
""", Exception],
[
 """
 start
 name = someone
 age = 18
 id = 23
 end
 start
 name = someoneelse
 age = 45
 id = 55
 end
 start
 name = lastname
 age = 34
 id = 5
 end
""", None]
]
for text, is_error in tests:    
   try:
     _ = Parser(text)
   except:
     assert is_error == Exception
   else:
     assert is_error is None

print('all tests passed')

Вывод:

all tests passed
0 голосов
/ 28 мая 2018

Самый простой алгоритм, который я мог бы придумать, если я правильно понял ваш вопрос.

d ={}
arr = []

for line in plines:
  if line == 'start':
    continue
  elif line =='end':
    arr.append(d)
    continue
  else:
    list_key_value = line.split('=')    
    d[list_key_value[0]] = int(list_key_value[1]) if 
    type(list_key_value[1]) == 'int' else str(list_key_value[1])
print (arr)

Вывод: [{'id': '7', 'date': '23.05.2018', 'summ': '500', 'owner': 'guest'}, {'id': '7', 'date': '23.05.2018', 'summ': '500', 'owner': 'guest'}]

0 голосов
/ 28 мая 2018

Использование Regex .

import re

with open(filename, "r") as infile:
    data = infile.read()
    data = re.findall("(?<=\\bstart\\b).*?(?=\\bend\\b)", data, flags=re.DOTALL)   #Find the required data from text

r = []
for i in data:
    val =  filter(None, i.split("\n"))
    d = {}
    for j in val:
        s = j.split("=")    #Split by "=" to form key-value pair
        d[s[0]] = s[1]
    r.append(d)             #Append to list
print(r)

Выход:

[{'date': '21.05.2018', 'summ': '500', 'id': '1'}, {'date': '23.05.2018', 'owner': 'guest', 'summ': '500', 'id': '7'}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...