Чтение записей распределено по нескольким строкам ввода в Python - PullRequest
2 голосов
/ 12 марта 2012

У меня очень неструктурированный файл текстовых данных с записями, которые обычно занимают несколько строк ввода.

  • Каждая запись имеет поля , разделенные пробелами , как и для обычного текста, поэтому каждое поле должно распознаваться по дополнительной информации, а не "разделителем полей csv".
  • Многие различные записи также имеют первые два поля , которые:
    • номер дня месяца (от 1 до 31);
    • первые три буквы месяца.
  • Но я знаю, что эта «специальная» запись с полем дня месяца и полем префикса месяца , за которой следуют записи , относящиеся к той же самой «отметке времени» ( день / месяц ), которые не содержат эту информацию .
  • Я точно знаю, что третье поле связано с неструктурированными предложениями из многих слов, таких как «операция, выполненная с этим инструментом на этом месте по этой причине»
  • Я знаю, что каждая запись может иметь одно или два числовых поля в качестве последних полей.
  • Я также знаю, что каждая новая запись начинается с новой строки (как первая запись дня / месяца, так и следующие записи того же дня / месяца).

Итак, чтобы подвести итог, каждая запись должна быть преобразована в запись CSV, подобную этой структуре: ДД, ММ, неструктурированный текст бла бла бла, номер1, номер2

Пример данных следующий:

> 20 Sep This is the first record, bla bla bla 10.45 
> Text unstructured
> of the second record bla bla
> 406.25 10001 
> 6 Oct Text of the third record thatspans on many 
> lines bla bla bla 60 
> 28 Nov Fourth 
> record 
> 27.43 
> Second record of the
> day/month BUT the fifth record of the file 500 90.25

Я разработал следующий синтаксический анализатор в Python, но не могу понять, как прочитать несколько строк входного файла, чтобы логически рассматривать их как уникальную часть информации. Я думаю, что я должен использовать два цикла один внутри другого, но я не могу справиться с индексами цикла.

Большое спасибо за помощь!

# I need to deal with is_int() and is_float() functions to handle records with 2 numbers
# that must be separated by a csv_separator in the output record...

import sys

days_in_month = range(1,31)
months_in_year = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']

csv_separator = '|'

def is_month(s):
    if s in months_in_year:
        return True
    else:
        return False 


def is_day_in_month(n_int):
    try:
        if int(n_int) in days_in_month:
            return True
        else:
            return False
    except ValueError:
        return False

#file_in = open('test1.txt','r')
file_in = open(sys.argv[1],'r')
#file_out = open("out_test1.txt", "w") # Use "a" instead of "w" to append to file
file_out = open(sys.argv[2], "w") # Use "a" instead of "w" to append to file

counter = 0
for line in file_in:
    counter = counter + 1
    line_arr = line.split()
    date_str = ''
    if is_day_in_month(line_arr[0]):
        if len(line_arr) > 1 and is_month(line_arr[1]):
            # Date!
            num_month = months_in_year.index(line_arr[1]) + 1
            date_str = '%02d' % int(line_arr[0]) + '/' + '%02d' % num_month + '/' + '2011' + csv_separator
        elif len(line_arr) > 1:
            # No date, but first number less than 31 (number of days in a month)
            date_str = ' '.join(line_arr) + csv_separator
        else:
            # No date, and there is only a number less than 31 (number of days in a month)
            date_str = line_arr[0] + csv_separator
    else:
        # there is not a date (a generic string, or a number higher than 31)
        date_str = ' '.join(line_arr) + csv_separator
    print >> file_out, date_str + csv_separator + 'line_number_' + str(counter)

file_in.close()
file_out.close()

Ответы [ 2 ]

2 голосов
/ 13 марта 2012

Вы можете использовать что-то вроде этого, чтобы переформатировать введенный текст. Код, скорее всего, может использовать некоторую очистку в зависимости от того, что допустимо в вашем вводе.

list = file_in.readlines()
list2 = []     
string =""
i = 0

while i < len(list):
   ## remove any leading or trailing white space then split on ' '
   line_arr = list[i].lstrip().rstrip().split(' ')

Возможно, вам придется изменить эту часть, потому что здесь я предполагаю, что запись должна заканчиваться хотя бы одним номером. Также некоторые люди недовольны попыткой / за исключением того, что их используют так (Эта часть из Как проверить, является ли строка числом (с плавающей точкой) в Python? )

   ##check for float at end of line
   try:
      float(line_arr[-1])
   except ValueError:
      ##not a float 
      ##remove new line and add to previous line
      string = string.replace('\n',' ') +  list[i]
   else:
      ##there is a float at the end of current line
      ##add to previous then add record to list2
      string = string.replace('\n',' ') +  list[i]
      list2.append(string)
      string = ""
   i+=1

Выходные данные, добавленные в ваш код:

20/09/2011||line_number_1
Text unstructured of the second record bla bla 406.25 10001||line_number_2
06/10/2011||line_number_3
28/11/2011||line_number_4
Second record of the day/month BUT the fifth record of the file 500 90.25||line_number_5

Я думаю, это близко к тому, что вы ищете.

0 голосов
/ 13 марта 2012

Я считаю, что это решение, которое использует некоторые из основ вашего подхода.Когда он распознает дату, он сбрасывает ее с начала строки и сохраняет ее для последующего использования.Точно так же он пересекает числовые элементы с правых концов строк, когда они присутствуют, оставляя неструктурированный текст.

lines = '''\
20 Sep This is the first record, bla bla bla 10.45 
Text unstructured
of the second record bla bla
406.25 10001 
6 Oct Text of the third record thatspans on many 
lines bla bla bla 60 
28 Nov Fourth 
record 
27.43 
Second record of the
day/month BUT the fifth record of the file 500 90.25'''

from string import split, join

days_in_month = [ str ( item ) for item in range ( 1, 31 ) ]
months_in_year = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]

lines = [ line . strip ( ) for line in split ( lines, '\n' ) if line ]

previous_date = None
previous_month = None
for line in lines :
    item = split ( line )
    #~ print item
    if len ( item ) >= 2 and item [ 0 ] in days_in_month and item [ 1 ] in months_in_year :
        previous_date = item [ 0 ] 
        previous_month = item [ 1 ] 
        item . pop ( 0 )
        item . pop ( 0 )
    try :
        number_2 = float ( item [ -1 ] )
        item . pop ( -1 )
    except :
        number_2 = None
    number_1 = None
    if not number_2 is None :
        try :
            number_1 = float ( item [ -1 ] )
            item . pop ( -1 )
        except :
            number_1 = None
    if number_1 is None and not number_2 is None :
        number_1 = number_2
        number_2 = None
    if number_1 and number_1 == int ( number_1 ) : number_1 = int ( number_1 )
    if number_2 and number_2 == int ( number_2 ) : number_2 = int ( number_2 )
    print previous_date, previous_month, join ( item ), number_1, number_2 
...