Как проанализировать данные в файле с разделителями переменной длины? - PullRequest
1 голос
/ 13 августа 2010

У меня есть текстовый файл, который не соответствует стандартам. Итак, я знаю (конец, начало) позиции каждого значения столбца.

Пример текстового файла:

#     #   #   #
Techy Inn Val NJ

Нашел позицию # используя этот код:

  1 f = open('sample.txt', 'r')
  2 i = 0
  3 positions = []
  4 for line in f:
  5     if line.find('#') > 0:
  6         print line
  7         for each in line:
  8             i += 1
  9             if each == '#':
 10                 positions.append(i)

1 7 11 15 => Позиции

Пока все хорошо! Теперь, как мне выбрать значения из каждой строки на основе выбранных позиций? Я пытаюсь построить эффективный цикл, но любые указатели очень ценятся, ребята! Спасибо (:

Ответы [ 7 ]

3 голосов
/ 13 августа 2010

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

>>> import re
>>> s="Techy Inn Val NJ"
>>> var1,var2,var3,var4 = re.match("(.{5}) (.{3}) (.{3}) (.{2})",s).groups()
>>> var1
'Techy'
>>> var2
'Inn'
>>> var3
'Val'
>>> var4
'NJ'
>>> 
2 голосов
/ 13 августа 2010

с макушки головы:

f = open(.......)
header = f.next() # get first line
posns = [i for i, c in enumerate(header + "#") if c = '#']
for line in f:
    fields = [line[posns[k]:posns[k+1]] for k in xrange(len(posns) - 1)]

Обновление с проверенным, фиксированным кодом:

import sys
f = open(sys.argv[1])
header = f.next() # get first line
print repr(header)
posns = [i for i, c in enumerate(header) if c == '#'] + [-1]
print posns
for line in f:
    posns[-1] = len(line)
    fields = [line[posns[k]:posns[k+1]].rstrip() for k in xrange(len(posns) - 1)]
    print fields

Входной файл:

#      #  #
Foo    BarBaz
123456789abcd

Отладочный вывод:

'#      #  #\n'
[0, 7, 10, -1]
['Foo', 'Bar', 'Baz']
['1234567', '89a', 'bcd']

Примечания по Robustification:

  1. Это решение обслуживает любой старый мусор (или ничего) после последнего # в строке заголовка; строка заголовка не должна быть дополнена пробелами или чем-то еще.
  2. ОП должен учитывать, является ли это ошибкой, если первый символ заголовка не #.
  3. У каждого поля есть конечные пробелы; это автоматически удаляет завершающий символ новой строки из поля rihtmost (и не запускает amok, если последняя строка не заканчивается символом новой строки).

Окончательное (?) Обновление: Leapfrooging @ gnibbler предлагает использовать slice(): настроить срезы один раз перед циклом.

import sys
f = open(sys.argv[1])
header = f.next() # get first line
print repr(header)
posns = [i for i, c in enumerate(header) if c == '#']
print posns
slices = [slice(lo, hi) for lo, hi in zip(posns, posns[1:] + [None])]
print slices
for line in f:
    fields = [line[sl].rstrip() for sl in slices]
    print fields
1 голос
/ 13 августа 2010

Хорошо, чтобы немного отличаться и дать обобщенное решение в комментариях, я использую строку заголовка вместо слайса и функции генератора.Кроме того, я позволил первым столбцам комментировать, не помещая имя поля в первый столбец и используя имена полей с несколькими символами вместо только '#'.

Минус в том, что поля одного символа не могут иметь имена заголовковно иметь только «#» в строке заголовка (которая всегда рассматривается как начало поля, даже после букв в заголовке)

sample="""
            HOTEL     CAT ST DEP ##
Test line   Techy Inn Val NJ FT  FT
"""
data=sample.splitlines()[1:]

def fields(header,line):
    previndex=0
    prevchar=''
    for index,char in enumerate(header):
        if char == '#' or (prevchar != char and prevchar == ' '):
            if previndex or header[0] != ' ':
                yield line[previndex:index]
            previndex=index
        prevchar = char
    yield line[previndex:]

header,dataline = data
print list(fields(header,dataline))

Вывод

['Techy Inn ', 'Val ', 'NJ ', 'FT  ', 'F', 'T']

Один практическийиспользование этого заключается в том, чтобы использовать при разборе данных фиксированной длины поля, не зная длины, просто поместив копию строки данных со всеми полями и без комментариев, а пробелы заменяются чем-то другим, например, '_', а значения односимвольных полей заменяются на #.

Заголовок из строки пробы:

'            Techy_Inn Val NJ FT  ##'
1 голос
/ 13 августа 2010

Адаптировано из ответа Джона Мачина

>>> header = "#     #   #   #"
>>> row = "Techy Inn Val NJ"
>>> posns = [i for i, c in enumerate(header) if c == '#']
>>> [row[slice(*x)] for x in zip(posns, posns[1:]+[None])]
['Techy ', 'Inn ', 'Val ', 'NJ']

Вы также можете написать последнюю строку, как это

>>> [row[i:j] for i,j in zip(posns, posns[1:]+[None])]

Для другого примера, который вы приводите в комментариях, вам просто нужно иметь правильный заголовок

>>> header = "#       #     #     #"
>>> row    = "Techiyi Iniin Viial NiiJ"
>>> posns = [i for i, c in enumerate(header) if c == '#']
>>> [row[slice(*x)] for x in zip(posns, posns[1:]+[None])]
['Techiyi ', 'Iniin ', 'Viial ', 'NiiJ']
>>> 
0 голосов
/ 13 августа 2010

Как насчет этого?

with open('somefile','r') as source:
    line= source.next()
    sizes= map( len, line.split("#") )[1:]
    positions = [ (sum(sizes[:x]),sum(sizes[:x+1])) for x in xrange(len(sizes)) ] 
    for line in source:
        fields = [ line[start,end] for start,end in positions ]

Это то, что вы ищете?

0 голосов
/ 13 августа 2010
f = open('sample.txt', 'r')
pos = [m.span() for m in re.finditer('#\s*', f.next())]
pos[-1] = (pos[-1][0], None)
for line in f:
   print [line[i:j].strip() for i, j in pos]
f.close()
0 голосов
/ 13 августа 2010
def parse(your_file):
    first_line = your_file.next().rstrip()
    slices = []
    start = None
    for e, c in enumerate(first_line):
        if c != '#':
            continue

        if start is None:
            start = e
            continue
        slices.append(slice(start, e))
        start = e
    if start is not None:
        slices.append(slice(start, None))

    for line in your_file:
        parsed = [line[s] for s in slices]
        yield parsed
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...