2012-03-12 45 views
2

我有一个高度非结构化的文本数据文件,其记录通常跨越多个输入行。阅读记录分布在Python中的多个输入行中

  • 每条记录​​的领域用空格隔开,作为普通的文本,所以每场必须通过额外的信息,而不是“CSV字段分隔符”被识别。
  • 许多不同的记录还共享前两个字段它们是:
    • 的月日(1到31)的数目;
    • 本月的前三个字母。
  • 但我知道,有一天的日场和月前缀场这个“特殊”的记录,然后与同一“时间戳”记载日/月)是不包含该信息
  • 我知道肯定第三场与非结构化句子像
  • 我知道,每一个记录可以有“用这个工具对地方为此进行操作”很多的话一个或两个数字作为最后一个字段的字段为
  • 我也知道每个新记录都以新行(这是日/月的第一条记录以及同一天/月的以下记录)开头。

因此,要总结,每个记录应转化为CSV记录类似这样的结构: DD,MM,非结构化文本唧唧歪歪,数字1,数字

的数据的一个例子是以下:

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

您还可能有所帮助:http://stackoverflow.com/questions/42950/get-last-day-of-the-month-in-python – 2012-03-12 19:53:32

+0

所以每一行是一个记录,是否正确?你能否依靠日志的文本块中没有数字字符? – 2012-03-12 19:58:43

+0

你应该看看pyparsing(http://pyparsing.wikispaces.com/)模块。 – Hooked 2012-03-12 20:46:01

回答

2

你可以使用类似的东西重新格式化输入文本。最有可能的代码可以使用一些基于输入中允许的内容进行清理。

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(' ') 

您可能需要更改此部分,因为这里我假设记录至少要以一个数字结尾。还有些人试过/不喜欢这样使用。 (这部分是从How do I check if a string is a number (float) in 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

感谢您的回复,因为它会将我驱动到正确答案,并分析“可能的输出记录”的底部。我无法弄清楚如何使用循环索引来处理file_in:counter = counter + 1 line_arr = line.split()'code'中的'code'list = file_in.readline()'code'和'code'像'code'line_arr [i]'code'。这就是为什么我认为我应该在外部循环内使用内部循环(处理像i ++这样的索引)。你能给我更多的细节吗? – TPPZ 2012-03-13 21:00:35

+0

抱歉,我不完全确定你在问什么。如果你想使用我提供的代码,你会在你的代码中将counter = 0放在代码之前,然后把你的for循环改成list2中的line:这应该给你上面的输出。 – malbani 2012-03-13 21:08:31

+0

对不起,我正在考虑内部循环,然后我重新组织代码,因为你建议我正确地把你的while循环之前我的计数器= 0.现在一切正常。再次感谢! – TPPZ 2012-03-14 19:14:53

0

我相信这是一个解决方案,它使用了您的方法的一些基本要素。当它识别出一个日期时,它会把它从线路的起点处丢掉并保存起来以备后用。同样,当它们出现在离开非结构化文本时,它会从行右端的数字项中跳跃。

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

感谢您的代码,但我有点困惑。我没有成功尝试在Python规则下正确地重新格式化它! – TPPZ 2012-03-13 23:07:23

+0

我想我刚刚才知道如何将格式化代码放入答案中。对不起,它很混乱。 – 2012-03-15 23:53:17