2015-09-23 7 views
2

У меня есть несколько очень больших не совсем файлов журнала csv.Разбор большого файла журнала pseudo csv в Python

С учетом следующих условий:

  1. поля значение имеют неэкранированные строки и запятые, почти все, что может быть в поле значения, включая «=»
  2. каждая веская линия имеет неизвестное количество допустимых полей значений
  3. допустимого значения выглядит key=value таким образом, что действительная линия выглядит как key1=value1, key2=value2, key3=value3 и т.д.
  4. начала каждой действительной линии должна начинаться с eventId=<some number>,

Каков наилучший способ для чтения файла, разбить файл на правильные строки и затем проанализировать каждую строку в правильные пары значений ключа?

Я попытался

file_name = 'file.txt' 
read_file = open(file_name, 'r').read().split(',\neventId') 

Это правильно разбирает первую запись, но все остальные записи начинается с =# вместо eventId=#. Есть ли способ сохранить разделитель и разделить на действительную строку новой строки?

Кроме того, скорость очень важна.

Пример данных:

eventId=123, key=value, key2=value2: 
this, will, be, a problem, 
maybe?=, 
anotherkey=anothervalue, 
eventId=1234, key1=value1, key2=value2, key3=value3, 
eventId=12345, key1= 
msg= {this is not a valid key value pair}, key=value, key21=value=, 

Да файл на самом деле это неаккуратно (иногда) каждое событие здесь имеет 3 пары ключ-значение, хотя в действительности существует неизвестное число пар ключ-значение в каждом случае.

+0

Я бы начал с разбивки на записи с использованием свойства 4. Например, разбить на eveyr экземпляр 'eventid = \ d +'. Оттуда это просто вопрос разделения с использованием регулярного выражения, которое соответствует « = », в словаре. – CollinD

+0

Я пытаюсь использовать свойство 4, чтобы разделить строки на чтение, но так, как я сейчас делаю это, удаляет разделитель. – deltap

+0

Поскольку разделитель статичен, вы всегда можете просто добавить его обратно.Я не очень хорошо знаком с Python, поэтому я не могу предоставить там тонну помощи, извините. – CollinD

ответ

-1

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

data_dict = {} 
file_lines = open('file.txt','r').readlines() 
for line in file_lines: 
    line_list = line.split(',') 
    if len(line_list)>=1: 
     if 'eventId' in line_list[0]: 
      for item in line_list: 
       pair = item.split('=') 
       data_dict.update({pair[0]:pair[1]}) 

Это должно быть сделано. Наслаждайтесь!

Если есть пробелы в «псевдо CSV» измените последнюю строку на:

data_dict.update({pair[0].split():pair[1].split()}) 

Для того, чтобы удалить пробелы из строки для ключа и значения.

p.s. Если это ответит на ваш вопрос, нажмите галочку слева, чтобы записать это как принятый ответ. Благодаря!

p.p.s. Набор строк из ваших фактических данных был бы очень полезен при написании чего-либо, чтобы избежать ошибок.

+0

IndexError: индекс списка за пределами допустимого диапазона Я не думаю, что вы учитываете символы новой строки в значениях. Я также не думаю, что вы принимаете во внимание, что в полях значений могут быть =, \ n и ','. – deltap

+0

Не могли бы вы показать нам несколько строк ваших данных? Я работаю по принципу «вид CSV» здесь. –

+0

попробовал ваше обновление: TypeError: unhashable type: 'list' Файл огромный, и я не могу раскрывать фактические строки (любые образцы могут не содержать всех случаев). Приведенные мной правила кажутся надежными. Мне пришлось бы генерировать некоторые «поддельные» данные. – deltap

0

Если начало каждой действительной линии должно начинаться с EVENTID = правильно, вы можете GroupBy этих строк и найти действительные пары с регулярным выражением:

from itertools import groupby 
import re 
with open("test.txt") as f: 
    r = re.compile("\w+=\w+") 
    grps = groupby(f, key=lambda x: x.startswith("eventId=")) 
    d = dict(l.split("=") for k, v in grps if k 
      for l in r.findall(next(v))[1:]) 
    print(d) 
    {'key3': 'value3', 'key2': 'value2', 'key1': 'value1', 'goodkey': 'goodvalue'} 

Если вы хотите сохранить Идентификаторы событий:

import re 
with open("test.txt") as f: 
    r = re.compile("\w+=\w+") 
    grps = groupby(f, key=lambda x: x.startswith("eventId=")) 
    d = list(r.findall(next(v)) for k, v in grps if k) 
    print(d) 
[['eventId=123', 'goodkey=goodvalue', 'key2=somestuff'], ['eventId=1234', 'key1=value1', 'key2=value2', 'key3=value3']] 

Не ясно из вашего описания именно то, что вывод должен быть, если вы хотите, чтобы все valids ключ = пары значений и если начало каждой действительной линии должна начинаться с EVENTID = не является точным:

from itertools import groupby,chain 
import re 
def parse(fle): 
    with open(fle) as f: 
     r = re.compile("\w+=\w+") 
     grps = groupby(f, key=lambda x: x.startswith("eventId=")) 
     for k, v in grps: 
      if k: 
       sub = "".join((list(v)) + list(next(grps)[1])) 
       yield from r.findall(sub) 

print(list(parse("test.txt"))) 

Выход:

['eventId=123', 'key=value', 'key2=value2', 'anotherkey=anothervalue', 
'eventId=1234', 'key1=value1', 'key2=value2', 'key3=value3', 
'eventId=12345', 'key=value', 'key21=value'] 
0

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

import re; 

in_string = """eventId=123, goodkey=goodvalue, key2=somestuff: 
this, will, be, a problem, 
maybe?=, 
anotherkey=anothervalue, gotit=see, 
the problem===s, 
eventId=1234, key1=value1, key2=value2, key3=value3, 
eventId=12345, key1= 
msg= {this is not a valid key value pair}, validkey=validvalue,""" 

line_matches = list(re.finditer(r'(,\n)?eventId=\d', in_string)) 

lines = [] 
for i in range(len(line_matches)): 
    match_start = line_matches[i].start() 
    next_match_start = line_matches[i+1].start() if i < len(line_matches)-1 else len(in_string)-1 
    line = in_string[match_start:next_match_start].lstrip(',\n') 
    lines.append(line) 

lineDicts = [] 
for line in lines: 
    d = {} 
    pad_line = ', '+line 
    matches = list(re.finditer(r', [\w\d]+=', pad_line)) 
    for i in range(len(matches)): 
     match = matches[i] 
     key = match.group().lstrip(', ').rstrip('=') 
     next_match_start = matches[i+1].start() if i < len(matches)-1 else len(pad_line) 
     value = pad_line[match.end():next_match_start] 
     d[key] = value 
    lineDicts.append(d) 

print lineDicts 

Выходы [{'eventId': '123', 'key2': 'somestuff:\nthis, will, be, a problem,\nmaybe?=,\nanotherkey=anothervalue', 'goodkey': 'goodvalue', 'gotit': 'see,\nthe problem===s'}, {'eventId': '1234', 'key2': 'value2', 'key1': 'value1', 'key3': 'value3'}, {'eventId': '12345', 'key1': '\nmsg= {this is not a valid key value pair}', 'validkey': 'validvalue'}]

+1

Спасибо, я отдам это, как только я вернусь домой. Я согласен с вами в 100% об использовании библиотек для ввода-вывода. Этот кошмар файла был экспортирован из стороннего программного обеспечения, написанного компанией, состоящей из 500 человек, которая останется безымянной. Как кто-то довольно новый для рабочей силы, я поражен неумением коммерческого программного обеспечения. – deltap

+0

Вы уверены, что это некомпетентность? Есть много дополнительной прибыли, которую можно сделать, если они могут убедить клиента в том, что что-то тривиально легко на самом деле сложно. Извините, вернитесь к техническим материалам сейчас. – nigel222

0

Если ваши ценности действительно могут содержать что-либо, нет однозначного способа синтаксического анализа. Любая пара key=value может быть частью предыдущего значения. Даже пара eventID=# на новой строке может быть частью значения из предыдущей строки.

Теперь, возможно, вы можете сделать «достаточно хороший» анализ данных, несмотря на двусмысленность, если вы предполагаете, что значения никогда не будут содержать действительных поисковых подстрок. Если вы знаете возможные ключи (или, по крайней мере, какие ограничения у них есть, например, буквенно-цифровые), будет намного легче угадать, что такое новый ключ и что является частью предыдущего значения.

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

with open("my_log_file") as infile: 
    text = infile.read() 

line_pattern = r'(?S)eventId=\d+,.*?(?:$|(?=\neventId=\d+))' 
kv_pattern = r'(?S)(\w+)=(.*?),\s*(?:$|(?=\w+=))' 
results = [re.findall(kv_pattern, line) for line in re.findall(line_pattern, text)] 

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

Если мы запустим это соответствие регулярных выражений на вашем примере текста, мы получаем:

[[('eventId', '123'), ('key', 'value'), ('key2', 'value2:\nthis, will, be, a problem,\nmaybe?='), ('anotherkey', 'anothervalue')], 
[('eventId', '1234'), ('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3')], 
[('eventId', '12345'), ('key1', '\nmsg= {this is not a valid key value pair}'), ('key', 'value'), ('key21', 'value=')]] 

maybe? не считается ключ из знака вопроса. msg и окончательные value не считаются ключами, потому что не было никаких запятых, отделяющих их от предыдущего значения.