在Python中解析非结构化文本

在Python中解析非结构化文本,python,parsing,text,Python,Parsing,Text,我想解析一个包含非结构化文本的文本文件。我需要知道地址、出生日期、姓名、性别和身份证 . 55 MORILLO ZONE VIII, BARANGAY ZONE VIII (POB.), LUISIANA, LAGROS F 01/16/1952 ALOMO, TERESITA CABALLES 3412-00000-A1652TCA2 12 . 22 FABRICANTE ST. ZONE VIII LUISIANA LAGROS, BARANGAY ZONE VIII (POB.),

我想解析一个包含非结构化文本的文本文件。我需要知道地址、出生日期、姓名、性别和身份证

. 55 MORILLO ZONE VIII,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
F
01/16/1952
ALOMO, TERESITA CABALLES
3412-00000-A1652TCA2
12    
. 22 FABRICANTE ST. ZONE
VIII LUISIANA LAGROS,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
M
10/14/1967
AMURAO, CALIXTO MANALO13
在上面的示例中,前3行是地址,只有“F”的行是性别,DOB是“F”后的行,DOB后是姓名,ID后是ID,ID下的12号是索引/记录号

但是,格式不一致。在第二组中,地址是4行而不是3行,并且索引/记录编号附加在名称之后(如果此人没有ID字段)

我想将文本改写为以下格式:

name, ID, address, sex, DOB

你必须利用文本的规律性和结构


我建议您一次读取一行并将其与正则表达式匹配,以确定其类型,并在person对象中填写适当的字段。写出该对象,并在得到已填写的字段时启动一个新对象。

使用正则表达式可以轻松完成此操作。如果您以前从未使用过它们,请查看python文档,然后启动redemo.py(在我的计算机上,它位于c:\python26\Tools\scripts中)

第一个任务是将平面文件拆分为一个实体列表(每个记录一个文本块)。从您提供的文本片段中,您可以使用与行首匹配的模式分割文件,其中第一个字符是点:

import re
re_entity_splitter = re.compile(r'^\.')

entities = re_entity_splitter.split(open(textfile).read())
请注意,该点必须转义(默认情况下为通配符)。还要注意图案前的r。r表示“原始字符串”格式,这使得您不必转义转义字符,从而导致所谓的“反斜杠瘟疫”

一旦你将文件分割成不同的人,挑选性别和出生日期就很容易了。使用这些:

re_gender     = re.compile(r'^[MF]')
re_birth_Date = re.compile(r'\d\d/\d\d/\d\d')

然后你就走了。您可以将平面文件粘贴到重新演示的GUI中,并尝试创建符合您需要的模式。你很快就会把它解析出来。一旦你掌握了这一点,你就可以使用符号组名(见文档)快速而清晰地挑选出单个元素。

这里有一个快速的破解工作

f = open('data.txt')

def process(file):
    address = ""

    for line in file:
        if line == '': raise StopIteration
        line = line.rstrip() # to ignore \n
        if line in ('M','F'):
            sex = line
            break
        else:
            address += line

    DOB = file.readline().rstrip() # to ignore \n
    name = file.readline().rstrip()

    if name[-1].isdigit():
        name = re.match(r'^([^\d]+)\d+', name).group(1)
        ID = None
    else:
        ID = file.readline().rstrip()
        file.readline() # ignore the record #

    print (name, ID, address, sex, DOB)

while True:
    process(f)

这可能有点过分,但针对这类问题的前沿机器学习算法是基于。例如


有一种软件使得训练这些模型相对容易。请参阅或。

这里是pyparsing解决方案()的第一个尝试。根据交错的注释,浏览单独的部分

data = """\
. 55 MORILLO ZONE VIII,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
F
01/16/1952
ALOMO, TERESITA CABALLES
3412-00000-A1652TCA2
12
. 22 FABRICANTE ST. ZONE
VIII LUISIANA LAGROS,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
M
10/14/1967
AMURAO, CALIXTO MANALO13
"""

from pyparsing import LineEnd, oneOf, Word, nums, Combine, restOfLine, \
    alphanums, Suppress, empty, originalTextFor, OneOrMore, alphas, \
    Group, ZeroOrMore

NL = LineEnd().suppress()
gender = oneOf("M F")
integer = Word(nums)
date = Combine(integer + '/' + integer + '/' + integer)

# define the simple line definitions
gender_line = gender("sex") + NL
dob_line = date("DOB") + NL
name_line = restOfLine("name") + NL
id_line = Word(alphanums+"-")("ID") + NL
recnum_line = integer("recnum") + NL

# define forms of address lines
first_addr_line = Suppress('.') + empty + restOfLine + NL
# a subsequent address line is any line that is not a gender definition
subsq_addr_line = ~(gender_line) + restOfLine + NL

# a line with a name and a recnum combined, if there is no ID
name_recnum_line = originalTextFor(OneOrMore(Word(alphas+',')))("name") + \
    integer("recnum") + NL

# defining the form of an overall record, either with or without an ID
record = Group((first_addr_line + ZeroOrMore(subsq_addr_line))("address") + 
    gender_line + 
    dob_line +
    ((name_line +
        id_line + 
        recnum_line) |
      name_recnum_line))

# parse data
records = OneOrMore(record).parseString(data)

# output the desired results (note that address is actually a list of lines)
for rec in records:
    if rec.ID:
        print "%(name)s, %(ID)s, %(address)s, %(sex)s, %(DOB)s" % rec
    else:
        print "%(name)s, , %(address)s, %(sex)s, %(DOB)s" % rec
print

# how to access the individual fields of the parsed record
for rec in records:
    print rec.dump()
    print rec.name, 'is', rec.sex
    print
印刷品:

ALOMO, TERESITA CABALLES, 3412-00000-A1652TCA2, ['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS'], F, 01/16/1952
AMURAO, CALIXTO MANALO, , ['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS'], M, 10/14/1967

['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS', 'F', '01/16/1952', 'ALOMO, TERESITA CABALLES', '3412-00000-A1652TCA2', '12']
- DOB: 01/16/1952
- ID: 3412-00000-A1652TCA2
- address: ['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS']
- name: ALOMO, TERESITA CABALLES
- recnum: 12
- sex: F
ALOMO, TERESITA CABALLES is F

['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS', 'M', '10/14/1967', 'AMURAO, CALIXTO MANALO', '13']
- DOB: 10/14/1967
- address: ['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS']
- name: AMURAO, CALIXTO MANALO
- recnum: 13
- sex: M
AMURAO, CALIXTO MANALO is M

谢谢我已经有了一些正则表达式的经验。如何处理地址部分?有些实体有3行或4行。一旦你将文件拆分成一个人员列表,我会为每个人尝试以下方法:1。将此人的文本拆分为第2行列表。对于每个人列表,当列表中的最后一项与re_性别不匹配时,将该项从列表末尾弹出。3.剩下的列表项是地址。lst=person.splitlines(),而不是re_-gender.search(lst[-1].strip()):lst.pop()lst.pop()address_list=lst在DOB=file.readline().rstrip()行失败,错误值错误:混合迭代和读取方法将丢失data@Francis:在这种情况下,只需将for循环转换为while循环并使用file.readline().rstrip()你好,保罗。谢谢你的解决方案!我想忽略/跳过一些以“#|*”开头的行。我该怎么做?你是说它们是以字符串“#|*”开头的吗?或者他们是从这些角色中的任何一个开始的?如果是第一个,则将注释定义为comment=“#|*”+restOfLine+NL;如果第二个注释定义为comment=one(“#|*”)+restOfLine+NL。然后做:记录。忽略(评论)-简单!