【问题标题】:Parse txt to blocks将 txt 解析为块
【发布时间】:2018-05-28 12:41:54
【问题描述】:

我有一个txt文件,结构如下

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 类型或日期:将其转换为字符串))。即用startend在块上拆分它,然后在=符号上拆分它。 start end 之间的行数可以不同。 D 但是a无法意识到。我试过这样的事情:

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

有人可以帮我吗?谢谢

【问题讨论】:

  • 欢迎来到 StackOverflow。您想要的结果结构不是很清楚——您应该从给定的示例 txt 文件中显示想要的结果。例如,您希望字典值采用什么格式? idsumm 键的值是整数还是字符串? date 键的值是字符串吗?如果一条记录缺少一个键,例如第一条记录中缺少owner,是否只是被跳过?另外,文件的格式是否保证没有错误?见How to create a Minimal, Complete, and Verifiable example
  • 我已经编辑了问题。字典:“str”:“str”。
  • 感谢您的编辑和评论——他们确实回答了我的问题。但是,如果您想继续在这里提问,您确实应该从示例输入中显示所需的输出,如How to create a Minimal, Complete, and Verifiable example 中所述。你的第一个问题很好,否则。

标签: python parsing dictionary


【解决方案1】:

使用正则表达式

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

【讨论】:

  • 如果字符串 'start' 或 'end' 包含在键或值之一中,这不会失败吗? IE。在您的findall 中,您不应该确保“开始”和“结束”单独在线吗?
  • @RoryDaulton。查看 OP 文本中的数据,内容由 start-end 块分隔。如果键值具有块名称,则 OP 应更改文本的格式
  • 我的意思是,例如,记录中的“所有者”可能是“机智”。我相信这会导致记录中任何后来的键值对被忽略。为处理此问题而对代码所做的更改非常小——我相信只需将findall 字符串更改为"^start$(.*?)^end$"。我不是正则表达式专家,所以我可能是错的,所以我的问题实际上是问题。
  • 哦,谢谢先生。我现在知道了。我现在使用\b 来获得完全匹配:)
  • 使用\b 不能解决@RoryDaulton 的例子:\bend\b 仍然匹配“wit's-end”。
【解决方案2】:

你也可以试试这样的:

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

【讨论】:

    【解决方案3】:

    我能想到的最简单的算法,如果你的问题没猜错的话。

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

    【讨论】:

    • 谢谢你,但某处有错误,导致输出具有相同的数据。
    • arr.append(d)之后,你需要d = {}
    • 另外,line.split('=') 总是返回一个字符串列表,所以type(list_key_value[1]) == 'int' 永远不会是真的(无论如何,OP 要求一个str:str 字典)。此外,strstr(list_key_value[1]) 中是不必要的。所以你可以说d[list_key_value[0]] = list_key_value[1]
    【解决方案4】:

    您可以构建一个简单的递归解析器,尝试在 startend 块之间查找数据:

    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
    

    【讨论】:

      【解决方案5】:

      只要保留一些上下文,您就可以简单地解析文本文件:在每个 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
      

      文件中的语法错误(缺少开始行或结束行,其他不正确的行)在这里不处理:

      • 缺少结束行将导致之前的 key=val 行被丢弃
      • 缺少起始行将导致异常,因为 None 不可下标 在下一个 key=val 行
      • 另一个不正确的行(没有= 符号)会导致异常ValueError: not enough values to unpack (expected 2, got 1)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-07-11
        • 1970-01-01
        • 1970-01-01
        • 2016-04-07
        • 2017-08-07
        • 1970-01-01
        • 2015-12-31
        相关资源
        最近更新 更多