2009-09-14 2 views
3

구조화되지 않은 텍스트가 포함 된 텍스트 파일을 구문 분석하려고합니다. 주소, 생년월일, 이름, 성별, 신원 확인이 필요합니다. 위의 예에서 Python에서 구조화되지 않은 텍스트 구문 분석

. 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는 DOB 후 "F", 이름, ID 후 줄 것 이름 뒤에, 그리고 아니오. ID 아래의 12는 색인/레코드 번호입니다.

그러나 형식이 일관되지 않습니다. 두 번째 그룹에서 주소는 3 대신 4 줄이고 색인/레코드 번호는 4입니다. (ID 필드가없는 경우) 이름 다음에 추가됩니다.

name, ID, address, sex, DOB 

답변

12

여기에는 pyparsing 솔루션 (easy-to-copy code at the pyparsing pastebin)의 첫 번째 스탭이 있습니다. 인터리브 코멘트에 따라 별도의 부품을 살펴보십시오.

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 
+0

하이 폴. 솔루션을 가져 주셔서 감사합니다! "# | *"로 시작하는 줄을 무시하거나 건너 뛰고 싶습니다. 어떻게해야합니까? – Francis

+1

"# | *"문자열로 시작한다는 의미입니까? 아니면이 캐릭터 중 어느 하나부터 시작해야할까요? 첫 번째 경우 주석을 comment = "# | *"+ restOfLine + NL로 정의하십시오. 두 번째가 주석을 comment = oneOf ("# | *") + restOfLine + NL로 정의하면. 그럼 : 할 record.ignore (의견) - 쉬운 - peasy! – PaulMcG

4

당신이 어떤 규칙 성을 악용 텍스트가 있나요 구조해야 :

나는 다음과 같은 형식으로 텍스트를 다시 작성하고 싶었다.

한 번에 한 줄을 읽고 정규식과 일치시켜 유형을 결정하고 person 객체의 해당 필드를 채우는 것이 좋습니다. 그 객체를 쓰고 이미 채운 필드를 얻을 때마다 새 객체를 시작하는 것입니다.

2

너무 쉽게 정규 표현식을 사용하여이 작업을 수행 할 수 있습니다. 전에 사용 해본 적이 없다면 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에 붙여 넣고 필요한 패턴과 일치하는 실험을 할 수 있습니다. 당신은 그것을 즉시 파싱 할 것입니다. 일단이 작업을 잘 수행하면 기호 그룹 이름 (docs 참조)을 사용하여 개별 요소를 빠르고 깨끗하게 추출 할 수 있습니다.

+0

감사합니다. 이미 정규 표현식에 대한 경험이 있습니다. 주소 부분을 어떻게 처리합니까? 일부 엔티티에는 3 또는 4 개의 행이 있습니다. – Francis

+0

파일을 사람 목록으로 분할하면 각 사람에 대해 다음을 시도합니다. 1. 사람의 텍스트를 줄 목록으로 나눕니다. 2. 각 사람 목록에 대해 목록의 마지막 항목에 ' re_gender와 일치하는 항목을 목록의 끝에서 꺼냅니다. 3. 나머지 목록 항목은 주소입니다. (LST [-1] .strip())하지 않으면 서 re_gender.search LST의 person.splitlines =() : lst.pop() lst.pop() = – twneale

1

다음은 빠른 해킹 작업입니다.

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) 
+0

LST는 라인 DOB에 실패를 ADDRESS_LIST = file.readline(). Error가있는 rstrip() ValueError : 반복 및 읽기 방법을 혼합하면 데이터가 손실됩니다. – Francis

+0

@Francis : for 루프를 while 루프로 돌리고 file.readline(). rstrip()을 사용하면됩니다. – Unknown

관련 문제