Python: анализ файла с разделителями двоеточиями с различным количеством полей

Я пытаюсь разобрать несколько файлов следующего формата в 'clientname'.txt

hostname:comp1
time: Fri Jan 28 20:00:02 GMT 2011
ip:xxx.xxx.xx.xx
fs:good:45
memory:bad:78
swap:good:34
Mail:good

Каждый раздел разделен символом :, но там, где строки 0,2,6 имеют 2 поля... строки 1,3-5 имеют 3 или более полей. (Большая проблема, с которой у меня возникли проблемы, - это строка time:, поскольку 20:00:02 - это действительно время, а не 3 отдельных поля.

У меня есть несколько таких файлов, которые мне нужно проанализировать. В некоторых из этих файлов есть еще много строк с несколькими полями.

...
for i in clients:
if os.path.isfile(rpt_path + i + rpt_ext):          # if the rpt exists then do this
    rpt = rpt_path + i + rpt_ext
    l_count = 0
    for line in open(rpt, "r"):
        s_line = line.rstrip()
        part = s_line.split(':')
        print part
        l_count = l_count + 1
else:                                               # else break
    break

Сначала я проверяю, существует ли файл, и если он существует, то открываю файл и анализирую его (в конце концов). На данный момент я просто печатаю вывод (часть печати), чтобы убедиться, что он правильно анализируется. Честно говоря, единственная проблема, с которой я сейчас сталкиваюсь, это время: поле. Как я могу относиться к этой строке конкретно иначе, чем ко всем остальным? Поле времени ВСЕГДА находится на второй строке во всех моих файлах отчетов.


person MarkP    schedule 28.01.2011    source источник


Ответы (4)


Если time является особым случаем, вы можете сделать:

[...]
s_line = line.rstrip()
if line.startswith('time:'):
    part = s_line.split(':', 1)
else:
    part = s_line.split(':')
print part
[...]

Это даст вам:

['hostname', 'comp1']
['time', ' Fri Jan 28 20:00:02 GMT 2011']
['ip', 'xxx.xxx.xx.xx']
['fs', 'good', '45']
['memory', 'bad', '78']
['swap', 'good', '34']
['Mail', 'good']

И не зависит от положения time в файле.

person Johnsyweb    schedule 28.01.2011
comment
Круто, получилось :) Полагаю, мой следующий вопрос будет заключаться в том, как я могу ссылаться на новые поля по мере их появления? т.е. Если бы я хотел распечатать только само время? это будет что-то вроде части [1] или части (1)? Или, точнее, как мне узнать, сколько полей имеет каждый список? - person MarkP; 29.01.2011
comment
@Johnsyweb: а что произойдет, если будет строка timeout : 987 или timer : off? Что это за начальный пробел перед данными о времени? - person John Machin; 29.01.2011
comment
Еще лучше было бы if line.startswith('time:') , чтобы предотвратить совпадение 'timers:foo:bar'. - person Johnsyweb; 29.01.2011
comment
@John Machin: Вы, должно быть, отправили это, когда я начал печатать. Обновил мой ответ соответственно. - person Johnsyweb; 29.01.2011
comment
В приведенном выше примере есть начальный пробел. Это может быть удалено при необходимости. - person Johnsyweb; 29.01.2011

Метод split имеет следующий синтаксис split( [sep [,maxsplit]]), и если указано значение maxsplit, он создаст части maxsplit+1. В вашем случае вы просто указали maxsplit как 1. Просто split(':',1) решит вашу проблему.

person Senthil Kumaran    schedule 28.01.2011
comment
Я попробовал это сначала, но в случаях, когда строка Swap: Good: 39, она возвращает ['Swap', 'Good: 39'] - person MarkP; 29.01.2011
comment
@Mark, если дата является единственной причиной этой проблемы, вы можете проверить if 'time' in line: part = s_line.split(':',1), в противном случае используйте обычное разделение. - person Senthil Kumaran; 29.01.2011

Соображения по дизайну:

Надежно обрабатывайте посторонние пробелы, включая пустые строки и отсутствующие двоеточия.

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

>>> def munched(s, n=None):
...     if n is None:
...         n = 99999999 # this kludge should not be necessary
...     return [x.strip() for x in s.split(':', n)]
...
>>> def parse_line(line):
...     if ':' not in line:
...         return [line.strip(), '']
...     record_type, remainder = munched(line, 1)
...     if record_type == 'time':
...         data = [remainder]
...     else:
...         data = munched(remainder)
...     return record_type, data
...
>>> for guff in """
... hostname:comp1
... time: Fri Jan 28 20:00:02 GMT 2011
... ip:xxx.xxx.xx.xx
... fs:good:45
...     memory   :    bad   :   78
... missing colon
... Mail:good""".splitlines(True):
...    print repr(guff), parse_line(guff)
...
'\n' ['', '']
'hostname:comp1\n' ('hostname', ['comp1'])
'time: Fri Jan 28 20:00:02 GMT 2011\n' ('time', ['Fri Jan 28 20:00:02 GMT 2011'])
'ip:xxx.xxx.xx.xx\n' ('ip', ['xxx.xxx.xx.xx'])
'fs:good:45\n' ('fs', ['good', '45'])
'    memory   :    bad   :   78    \n' ('memory', ['bad', '78'])
'missing colon\n' ['missing colon', '']
'Mail:good' ('Mail', ['good'])
>>>
person John Machin    schedule 29.01.2011
comment
@Johnsyweb: YAGNI прямо из знаменитых последних слов. Он игнорирует 256-е следствие закона Мерфи: если пользователи, разработчики и боссы с остроконечными волосами могут вносить идиосинкразические изменения в структуру данных, они это сделают. - person John Machin; 29.01.2011
comment
Отсюда мой плюс. Однако, если OP владеет данными и парсером, больше нечего делать. Пока что. - person Johnsyweb; 29.01.2011

Если поле времени всегда 2-я строка. Почему нельзя его пропустить и разобрать отдельно?

Что-то типа

for i, line in enumerate(open(rpt, "r").read().splitlines()):
    if i==1: # Special parsing for time: line
        data = line[5:]
    else:
        # your normal parsing logic
person Dat Chu    schedule 28.01.2011
comment
Мне пришлось бы пропускать каждую строку с более чем 1 : разделителем, а строки с более чем 1 : разделителем не всегда находятся в одном и том же месте в файле. - person MarkP; 29.01.2011
comment
У меня возникли проблемы с тем, что вы пытаетесь выполнить. Вы пытаетесь обрабатывать все строки, которые имеют более одного : по-разному (кроме строки time:)? - person Dat Chu; 29.01.2011
comment
Я полагаю, что решение будет состоять в том, чтобы проверять каждую строку на наличие : , а если их больше 3, обрабатывать ее отдельно? Не уверен, как это сделать. - person MarkP; 29.01.2011