Запись/разбор файла фиксированной ширины с использованием Python
Я новичок на Python, и я смотрю, как использовать его для написания волосатого материала EDI, который требуется нашему поставщику.
В основном они нуждаются в текстовом файле с фиксированной шириной 80 символов с определенными "кусками" поля с данными, а остальные остаются пустыми. У меня есть документация, поэтому я знаю, какова длина каждого "куска". Ответ, который я получаю, легче анализировать, поскольку у него уже есть данные, и я могу использовать "срезы" Python, чтобы извлечь то, что мне нужно, но я не могу назначить срез - я пробовал это уже потому, что это звучало как хороший решение, и это не сработало, поскольку строки Python неизменяемы:)
Как я уже сказал, я действительно новичок на Python, но я очень рад узнать об этом:) Как бы я это сделал? В идеале я хотел бы сказать, что диапазон 10-20 равен "Foo", и пусть это будет строка "Foo" с 7 дополнительными пробельными символами (при условии, что указанное поле имеет длину 10), и это будет часть более крупного 80-символьного поля, но я не уверен, как делать то, что я думаю.
Ответы
Ответ 1
Вам не нужно назначать срезы, просто создайте строку, используя % formatting
.
Пример с фиксированным форматом для 3 элементов данных:
>>> fmt="%4s%10s%10s"
>>> fmt % (1,"ONE",2)
' 1 ONE 2'
>>>
То же самое, ширина поля, поставляемая с данными:
>>> fmt2 = "%*s%*s%*s"
>>> fmt2 % (4,1, 10,"ONE", 10,2)
' 1 ONE 2'
>>>
Разделите данные и ширину поля и используйте тэки zip()
и str.join()
:
>>> widths=(4,10,10)
>>> items=(1,"ONE",2)
>>> "".join("%*s" % i for i in zip(widths, items))
' 1 ONE 2'
>>>
Ответ 2
Надеюсь, я понимаю, что вы ищете: каким-то образом удобно идентифицировать каждую часть строки с помощью простой переменной, но выводить ее с нужной шириной?
Ниже приведенный ниже фрагмент может дать вам то, что вы хотите
class FixWidthFieldLine(object):
fields = (('foo', 10),
('bar', 30),
('ooga', 30),
('booga', 10))
def __init__(self):
self.foo = ''
self.bar = ''
self.ooga = ''
self.booga = ''
def __str__(self):
return ''.join([getattr(self, field_name).ljust(width)
for field_name, width in self.fields])
f = FixWidthFieldLine()
f.foo = 'hi'
f.bar = 'joe'
f.ooga = 'howya'
f.booga = 'doin?'
print f
Это дает:
hi joe howya doing
Он работает, сохраняя переменную уровня класса fields
, которая записывает порядок, в котором каждое поле должно появляться на выходе, вместе с количеством столбцов, которое должно иметь это поле. В __init__
есть соответствующие имена переменных экземпляра, которые изначально устанавливаются в пустую строку.
Метод __str__
выводит эти значения в виде строки. Он использует понимание списка над атрибутом fields
на уровне класса, просматривая значение экземпляра для каждого поля по имени, а затем выравнивая его по левому краю в соответствии с столбцами. Полученный список полей затем объединяется пустой строкой.
Обратите внимание, что это не анализирует ввод, хотя вы можете легко переопределить конструктор, чтобы взять строку и проанализировать столбцы в соответствии с шириной поля и поля в fields
. Он также не проверяет значения экземпляра, длина которых превышает их выделенную ширину.
Ответ 3
Вы можете использовать justify функции для выравнивания по левому краю, выравнивания по правому краю и центрирования строки в поле заданной ширины.
'hi'.ljust(10) -> 'hi '
Ответ 4
Я знаю, что этот поток довольно старый, но мы используем библиотеку django-copybook. Это не имеет никакого отношения к django (больше). Мы используем его для перехода между файлами cobol с фиксированной шириной и питоном. Вы создаете класс для определения макета записи фиксированной ширины и можете легко перемещаться между типизированными объектами python и файлами с фиксированной шириной:
USAGE:
class Person(Record):
first_name = fields.StringField(length=20)
last_name = fields.StringField(length=30)
siblings = fields.IntegerField(length=2)
birth_date = fields.DateField(length=10, format="%Y-%m-%d")
>>> fixedwidth_record = 'Joe Smith 031982-09-11'
>>> person = Person.from_record(fixedwidth_record)
>>> person.first_name
'Joe'
>>> person.last_name
'Smith'
>>> person.siblings
3
>>> person.birth_date
datetime.date(1982, 9, 11)
Он также может обрабатывать ситуации, подобные функциям Cobol OCCURS, например, когда конкретный раздел повторяется X раз
Ответ 5
Немного сложно разобрать ваш вопрос, но я собираю, что вы получаете файл или файл-подобный объект, читая его и заменяя некоторые значения некоторыми результатами бизнес-логики. Правильно ли это?
Самый простой способ преодоления неизменяемости строк - написать новую строку:
# Won't work:
test_string[3:6] = "foo"
# Will work:
test_string = test_string[:3] + "foo" + test_string[6:]
Сказав это, для вас очень важно, чтобы вы что-то делали с этой строкой, но я точно не знаю, что это такое. Вы записываете его обратно в выходной файл, пытаясь редактировать файл на месте или что-то еще? Я объясняю это тем, что в результате создания новой строки (которая имеет одно и то же имя переменной, как и старая строка) необходимо подчеркнуть необходимость выполнения явной операции записи после преобразования.
Ответ 6
Вы можете преобразовать строку в список и выполнить манипуляции с фрагментами.
>>> text = list("some text")
>>> text[0:4] = list("fine")
>>> text
['f', 'i', 'n', 'e', ' ', 't', 'e', 'x', 't']
>>> text[0:4] = list("all")
>>> text
['a', 'l', 'l', ' ', 't', 'e', 'x', 't']
>>> import string
>>> string.join(text, "")
'all text'
Ответ 7
Легко написать функцию для "модификации" строки.
def change(string, start, end, what):
length = end - start
if len(what)<length: what = what + " "*(length-len(what))
return string[0:start]+what[0:length]+string[end:]
Использование:
test_string = 'This is test string'
print test_string[5:7]
# is
test_string = change(test_string, 5, 7, 'IS')
# This IS test string
test_string = change(test_string, 8, 12, 'X')
# This IS X string
test_string = change(test_string, 8, 12, 'XXXXXXXXXXXX')
# This IS XXXX string
Ответ 8
Я использовал пример Jarret Hardie и немного изменил его. Это позволяет выбрать тип выравнивания текста (слева, справа или по центру).
class FixedWidthFieldLine(object):
def __init__(self, fields, justify = 'L'):
""" Returns line from list containing tuples of field values and lengths. Accepts
justification parameter.
FixedWidthFieldLine(fields[, justify])
fields = [(value, fieldLenght)[, ...]]
"""
self.fields = fields
if (justify in ('L','C','R')):
self.justify = justify
else:
self.justify = 'L'
def __str__(self):
if(self.justify == 'L'):
return ''.join([field[0].ljust(field[1]) for field in self.fields])
elif(self.justify == 'R'):
return ''.join([field[0].rjust(field[1]) for field in self.fields])
elif(self.justify == 'C'):
return ''.join([field[0].center(field[1]) for field in self.fields])
fieldTest = [('Alex', 10),
('Programmer', 20),
('Salem, OR', 15)]
f = FixedWidthFieldLine(fieldTest)
print f
f = FixedWidthFieldLine(fieldTest,'R')
print f
Возврат:
Alex Programmer Salem, OR
Alex Programmer Salem, OR