Правильный способ чтения текстового файла на основе позиции
Итак, у меня есть файл с данными в этом (стандартизированном) формате:
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 спросил в отредактированном вопросе. Это предполагает, что символ формата является целым числом, но его можно легко обобщить для других обозначений формата.