Правильный способ чтения текстового файла на основе позиции

Итак, у меня есть файл с данными в этом (стандартизированном) формате:

 12455WE READ THIS             TOO796445 125997  554777     
 22455 888AND THIS       TOO796445 125997  55477778 2 1

Вероятно, придуманный кем-то, кто сделал слишком много кобола.

Каждое поле имеет фиксированную длину, и я могу прочитать его, отрезав строку.

Моя проблема в том, как я могу структурировать свой код таким образом, чтобы он был более гибким и не заставлял меня использовать жестко закодированные смещения для срезов? Должен ли я использовать класс констант чего-то подобного?

РЕДАКТИРОВАТЬ:

Также первое число (всегда присутствует от 0 до 9) определяет структуру линии фиксированной длины. Также файл предоставлен третьей стороной, которая обеспечивает достоверность, поэтому мне не нужно проверять формат, только прочитав его. Есть около 11 различных линейных структур.

3 ответа

Решение

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

Я предлагаю использовать словарь с 5-значным кодом типа строки. Каждое значение в словаре может быть списком смещений полей (или наборов (смещение, ширина)), проиндексированных по положению поля.

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

namedtuple s фактически реализованы как классы, но определяют новый namedtuple type гораздо более компактен, чем создание явного определения класса, и namedtuples использовать __slots__ протокол, поэтому они занимают меньше оперативной памяти, чем обычный класс, который использует __dict__ для хранения его атрибутов.


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

from collections import namedtuple

#Create a namedtuple, `Fields`, containing all field names
fieldnames = [
    'record_type', 
    'special',
    'communication',
    'id_number',
    'transaction_code',
    'amount',
    'other',
]

Fields = namedtuple('Fields', fieldnames)

#Some fake test data
data = [
    #          1         2         3         4         5
    #012345678901234567890123456789012345678901234567890123
    "12455WE READ THIS             TOO796445 125997  554777",
    "22455 888AND THIS       TOO796445 125997  55477778 2 1",
]

#A dict to store the field (offset, width) data for each field in a record,
#keyed by record type, which is always stored at (0, 5)
offsets = {}

#Some fake record structures
offsets['12455'] = Fields(
    record_type=(0, 5), 
    special=None,
    communication=(5, 28),
    id_number=(33, 6),
    transaction_code=(40, 6),
    amount=(48, 6),
    other=None)

offsets['22455'] = Fields( 
    record_type=(0, 5),
    special=(6, 3),
    communication=(9, 18),
    id_number=(27, 6),
    transaction_code=(34, 6),
    amount=(42, 8),
    other=(51,3))

#Test.
for row in data:
    print row
    #Get record type
    rt = row[:5]
    #Get field structure
    fields = offsets[rt]
    for name in fieldnames:
        #Get field offset data by field name
        t = getattr(fields, name)
        if t is not None:
            start, flen = t
            stop = start + flen
            data = row[start : stop]            
            print "%-16s ... %r" % (name, data)
    print

выход

12455WE READ THIS             TOO796445 125997  554777
record_type      ... '12455'
communication    ... 'WE READ THIS             TOO'
id_number        ... '796445'
transaction_code ... '125997'
amount           ... '554777'

22455 888AND THIS       TOO796445 125997  55477778 2 1
record_type      ... '22455'
special          ... '888'
communication    ... 'AND THIS       TOO'
id_number        ... '796445'
transaction_code ... '125997'
amount           ... '55477778'
other            ... '2 1'

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

formats = [
    [1, ],
    [1, 4, 28, 7, 7, 7],
]

def unfold(line):
    lengths = formats[int(line[0])]
    ends = [sum(lengths[0:n+1]) for n in range(len(lengths))]
    return [line[s:e] for s,e in zip([0] + ends[:-1], ends)]

lines = [
    "12455WE READ THIS             TOO796445 125997 554777",
]

for line in lines:
    print unfold(line)

Изменить: Обновлен код, чтобы лучше соответствовать тому, что maazza спросил в отредактированном вопросе. Это предполагает, что символ формата является целым числом, но его можно легко обобщить для других обозначений формата.

Другие вопросы по тегам