【问题标题】:Extracting data elements from large unstructured text files with Python使用 Python 从大型非结构化文本文件中提取数据元素
【发布时间】:2019-01-17 22:19:36
【问题描述】:

我正在尝试从没有一致分隔符的大型非结构化文本文件(每个文件 1,000,000 到 15,000,000 行)中提取数据元素。数据元素的顺序是一致的。

Sample data:

  NAME    FIRSTNAME LASTNAME    DATE-OF-BIRTH        01/01/2019   ID-NUMBER  123     
  ADDRESS-1  1234 FAKE STREET                        COUNTY-CODE    123                             
  ADDRESS-2                                                                            
  CITY       NOWHERE                STATE   OH   ZIP  12345                            
RANDOM DATA .... 700+ LINES
  NAME  FIRSTNAME2 LASTNAME2    DATE-OF-BIRTH        01/01/2019   ID-NUMBER 4567    
  ADDRESS-1           123456 OTHER STREET            COUNTY-CODE  45678                                  
  ADDRESS-2                                                                            
  CITY      SOMEWHERE               STATE   MI   ZIP  65432                            
RANDOM DATA .... 700+ LINES

我正在寻找一种方法来使用下面列出的几个字段的值创建 CSV 输出:

NAME, COUNTY-CODE, ZIP
FIRSTNAME LASTNAME, 123, 12345
FIRSTNAME2 LASTNAME2, 45678, 65432 

数据不是制表符分隔的,间距会有所不同。任何帮助将不胜感激!

【问题讨论】:

  • 当您说“RANDOM DATA.... 700+”行时,该数据是否也是 NAME FIRSTNAME LASTNAME.... 还是完全不同的东西?
  • 没有每个数据元素都是唯一的。当我放入“随机数据”时,它包含 700 多行其他客户信息和交易数据

标签: python parsing text export-to-csv


【解决方案1】:

嗯……

我假设您有一堆行,每行包含对 ID VALUE,并且每个 chunk 以 id NAME 开头。

所以我会使用re 模块来搜索预期的模式,即 NAME 开始一个新元素的出现。由于真实的名字和姓氏可以使用多个单词(约翰·菲茨杰拉德·肯尼迪),因此我将 NAME 视为 NAME 和 DATE-OF-BIRTH 之间的所有内容。

恕我直言,一种简单的方法是在解析行时构建一个字典,并在到达 NAME 时使用 DictWriter 写入字典,并在文件末尾。如果找到更多关键字,我只会保留每个关键字的第一次出现,但您也可以引发异常。

代码可以是

import re
import csv

# prepare the patterns to search for
name = re.compile(r"NAME\s+(.*)\s+DATE")
zip_code = re.compile(r"ZIP\s*([0-9]+)")
county_code = re.compile(r"COUNTY-CODE\s*([0-9]+)")

with open("input.txt") as fdin, open("output.csv", newline='') as fdout:
    wr = csv.DictWriter(fdout, fieldnames=['NAME', 'COUNTY-CODE', 'ZIP'])
    elt = {}
    wr.writeheader()
    for line in fdin:
        # process NAME
        mx = name.search(line)
        if mx:
            if elt:                    # write previous dict if any
                wr.writerow(elt)
            elt = {'NAME': mx.group(1).strip()}   # initialize a new dict
        # process other keywords
        if not 'COUNTY-CODE' in elt:              # only keep first one
            mx =  county_code.search(line)
            if mx:
                elt['COUNTY-CODE'] = mx.group(1).strip()   # update the dict with it
        if not 'ZIP' in elt:
            mx = zip_code.search(line)
            if mx:
                elt['ZIP'] = mx.group(1)
    wr.writerow(elt)                                # don't forget last dict

【讨论】:

    【解决方案2】:

    问题与in other SO question发现的问题非常相似。

    解决方案是构造部分语法,它解析已识别构造的结构,同时跳过无法识别的内容。

    在你的情况下,使用textX,这将是这样的(我没有测试过,但你明白了):

    from textx import metamodel_from_str
    
    mm = metamodel_from_str(r'''
        File: ( /(?s:.*?(?=NAME))/ persons*=Person | 'NAME' )*
              /(?s).*/;
    
        Person:
            'NAME' first_name=Name last_name=Name birth_date=Date
            'ADDRESS-1' address_1=UntilEOL
            'ADDRESS-2' address_2=UntilEOL
            'CITY' city=UntilEOL
        ;
    
        Name: /\w+/;
        Date: /\d{4}-\d{2}-\d{2}/;
        UntilEOL[noskipws]: /.*?\n/;
    ''')
    
    data_model = mm.model_from_file('some_input_file.txt')
    
    # Here data_model is an object with attribute `persons`
    # where each person have attributes `first_name`, `last_name`, ...
    # from the `Person` rule above.
    
    

    注意:此解决方案假设结构部分的开头必须有一个关键字NAME,但可以在随机数据中找到关键字,因为解析器将消耗规则Person 的无效解析NAME 这个词并继续。

    根据您的实际数据,您必须稍微调整语法(例如特定的正则表达式)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-19
      • 2014-05-01
      • 2021-11-29
      • 2016-09-07
      • 2016-02-15
      • 1970-01-01
      相关资源
      最近更新 更多