【问题标题】:Parsing data from text file从文本文件中解析数据
【发布时间】:2013-06-14 09:35:10
【问题描述】:

我有一个包含如下内容的文本文件:

******** ENTRY 01 ********
ID:                  01
Data1:               0.1834869385E-002
Data2:              10.9598489301
Data3:              -0.1091356549E+001
Data4:                715

然后是一个空行,并重复更多相似的块,它们都具有相同的数据字段。

我正在将一个C++代码移植到Python中,某部分逐行获取文件,检测文本标题,然后检测每个字段文本以提取数据。这看起来根本不像一个智能代码,我认为 Python 必须有一些库来轻松解析这样的数据。毕竟,它几乎看起来像 CSV!

对此有什么想法吗?

【问题讨论】:

    标签: python file parsing


    【解决方案1】:

    实际上,它与 CSV 相差甚远。

    您可以将文件用作迭代器;以下生成器函数产生完整的部分:

    def load_sections(filename):
        with open(filename, 'r') as infile:
            line = ''
            while True:
                while not line.startswith('****'): 
                    line = next(infile)  # raises StopIteration, ending the generator
                    continue  # find next entry
    
                entry = {}
                for line in infile:
                    line = line.strip()
                    if not line: break
    
                    key, value = map(str.strip, line.split(':', 1))
                    entry[key] = value
    
                yield entry
    

    这会将文件视为迭代器,这意味着任何循环都会将文件推进到下一行。外环仅用于从一个部分移动到另一个部分;内部 whilefor 循环完成所有实际工作;首先跳过行,直到找到 **** 标题部分(否则丢弃),然后遍历所有非空行以创建一个部分。

    循环使用函数:

    for section in load_sections(filename):
        print section
    

    在文本文件中重复您的示例数据会导致:

    >>> for section in load_sections('/tmp/test.txt'):
    ...     print section
    ... 
    {'Data4': '715', 'Data1': '0.1834869385E-002', 'ID': '01', 'Data3': '-0.1091356549E+001', 'Data2': '10.9598489301'}
    {'Data4': '715', 'Data1': '0.1834869385E-002', 'ID': '01', 'Data3': '-0.1091356549E+001', 'Data2': '10.9598489301'}
    {'Data4': '715', 'Data1': '0.1834869385E-002', 'ID': '01', 'Data3': '-0.1091356549E+001', 'Data2': '10.9598489301'}
    

    如果你愿意,你可以添加一些数据转换器;键到可调用的映射会做:

    converters = {'ID': int, 'Data1': float, 'Data2': float, 'Data3': float, 'Data4': int}
    

    然后在生成器函数中,而不是entry[key] = valueentry[key] = converters.get(key, lambda v: v)(value)

    【讨论】:

      【解决方案2】:

      我的文件:

      ******** ENTRY 01 ********
      ID:                  01
      Data1:               0.1834869385E-002
      Data2:              10.9598489301
      Data3:              -0.1091356549E+001
      Data4:                715
      
      ID:                  02
      Data1:               0.18348674325E-012
      Data2:              10.9598489301
      Data3:              0.0
      Data4:                5748
      
      ID:                  03
      Data1:               20.1834869385E-002
      Data2:              10.954576354
      Data3:              10.13476858762435E+001
      Data4:                7456
      

      Python 脚本:

      import re
      
      with open('my_file', 'r') as f:
          data  = list()
          group = dict()
          for key, value in re.findall(r'(.*):\s*([\dE+-.]+)', f.read()):
              if key in group:
                  data.append(group)
                  group = dict()
              group[key] = value
          data.append(group)
      
      print data
      

      打印输出:

      [
          {
              'Data4': '715',
              'Data1': '0.1834869385E-002',
              'ID': '01',
              'Data3': '-0.1091356549E+001',
              'Data2': '10.9598489301'
          },
          {
              'Data4': '5748',
              'Data1': '0.18348674325E-012',
              'ID': '02',
              'Data3': '0.0',
              'Data2': '10.9598489301'
          },
          {
              'Data4': '7456',
              'Data1': '20.1834869385E-002',
              'ID': '03',
              'Data3': '10.13476858762435E+001',
              'Data2': '10.954576354'
          }
      ]
      

      【讨论】:

      • 我认为这不会分组到记录中。
      • 非常花哨!但是我对 RegEx 过敏,所以由于我的局限性,我更喜欢 Martijn 解决方案。无论如何,感谢您的出色回答!
      • @RomanRdgz 谢谢!其实我爱上了正则表达式:)
      【解决方案3】:

      一个非常简单的方法可能是

      all_objects = []
      
      with open("datafile") as f:
          for L in f:
              if L[:3] == "***":
                  # Line starts with asterisks, create a new object
                  all_objects.append({})
              elif ":" in L:
                  # Line is a key/value field, update current object
                  k, v = map(str.strip, L.split(":", 1))
                  all_objects[-1][k] = v
      

      【讨论】:

      • 使用with open('datafile') as f:比使用open('datafile')更好更安全,因为它会自动close文件,即使出现异常。
      • @PeterVaro:更好、更安全、更长,而且(IMO)有点丑。无论如何都修复了,因为显然 Python 正在去那里
      猜你喜欢
      • 1970-01-01
      • 2016-07-07
      • 1970-01-01
      • 1970-01-01
      • 2017-07-04
      • 2015-12-31
      • 1970-01-01
      • 2022-06-15
      相关资源
      最近更新 更多