Написание / анализ файла фиксированной ширины с использованием Python - PullRequest
11 голосов
/ 11 мая 2009

Я новичок в Python, и я собираюсь использовать его для написания каких-то интересных EDI-материалов, которые требуются нашему поставщику.

В основном им нужен текстовый файл фиксированной ширины из 80 символов, при этом некоторые «фрагменты» поля с данными, а другие остаются пустыми. У меня есть документация, поэтому я знаю, какова длина каждого «куска». Ответ, который я получаю, легче проанализировать, поскольку в нем уже есть данные, и я могу использовать «кусочки» Python для извлечения того, что мне нужно, но я не могу назначить кусочек - я пробовал это уже, потому что это звучало как решение, и оно не сработало, так как строки Python неизменны:)

Как я уже сказал, я действительно новичок в Python, но я очень рад этому научиться :) Как бы я поступил так? В идеале я хотел бы иметь возможность сказать, что диапазон 10-20 равен «Foo» и иметь строку «Foo» с 7 дополнительными пробельными символами (при условии, что указанное поле имеет длину 10) и иметь это значение часть большего поля из 80 символов, но я не уверен, как делать то, что я думаю.

Ответы [ 8 ]

17 голосов
/ 11 мая 2009

Вам не нужно присваивать срезы, просто создайте строку, используя % 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'
>>> 
8 голосов
/ 11 мая 2009

Вы можете использовать justify функции для выравнивания по левому краю, выравнивания по правому краю и центрирования строки в поле заданной ширины.

'hi'.ljust(10) -> 'hi        '
8 голосов
/ 11 мая 2009

Надеюсь, я понимаю, что вы ищете: какой-нибудь способ удобно идентифицировать каждую часть строки с помощью простой переменной, но вывести ее с отступом до правильной ширины?

Фрагмент ниже может дать вам то, что вы хотите

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. Он также не проверяет значения экземпляров, длина которых превышает их выделенную ширину.

1 голос
/ 14 февраля 2017

Я знаю, что эта ветка довольно старая, но мы используем библиотеку под названием django-copybook . Это не имеет ничего общего с Джанго (больше). Мы используем его для перехода между файлами cobol с фиксированной шириной и python. Вы создаете класс для определения макета записи фиксированной ширины и можете легко перемещаться между типизированными объектами 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)

Он также может обрабатывать ситуации, подобные функциональности OCCURS в Cobol, например, когда определенный раздел повторяется X раз

0 голосов
/ 28 мая 2014

Я использовал пример Джаррета Харди и немного изменил его. Это позволяет выбрать тип выравнивания текста (слева, справа или по центру).

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
0 голосов
/ 11 мая 2009

Легко написать функцию для «изменения» строки.

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
0 голосов
/ 11 мая 2009

Вы можете преобразовать строку в список и выполнить манипуляцию срезами.

>>> 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'
0 голосов
/ 11 мая 2009

Немного сложно разобрать ваш вопрос, но я понимаю, что вы получаете файл или файл-подобный объект, читаете его и заменяете некоторые значения результатами бизнес-логики. Это правильно?

Самый простой способ преодолеть неизменность строки - это написать новую строку:

# Won't work:
test_string[3:6] = "foo"

# Will work:
test_string = test_string[:3] + "foo" + test_string[6:]

Сказав это, кажется, что для вас важно, чтобы вы что-то сделали с этой строкой, но я не совсем уверен, что это такое. Вы записываете его обратно в выходной файл, пытаетесь редактировать файл на месте или что-то еще? Я поднял этот вопрос, потому что процесс создания новой строки (которая, как оказалось, имеет то же имя переменной, что и старая строка) должен подчеркнуть необходимость выполнения явной операции записи после преобразования.

...