Escrever / analisar um arquivo de largura fixa usando Python
Pergunta
Eu sou um novato para Python e eu estou olhando para usá-lo para escrever algumas coisas EDI peludo que o nosso fornecedor exige.
Basicamente, eles precisam de um arquivo de texto de largura fixa de 80 caracteres, com determinados "pedaços" de campo com dados e outros deixados em branco. Eu tenho a documentação, então eu sei o que o comprimento de cada "pedaço" é. A resposta que eu recebo de volta é mais fácil de analisar, uma vez que já terá dados e eu posso usar "fatias" do Python para extrair o que eu preciso, mas eu não posso atribuir a uma fatia - Tentei que já porque soou como uma boa solução, e não funcionou desde cordas Python são imutáveis:)
Como eu disse eu sou realmente um novato para Python, mas eu estou animado sobre a aprendizagem que :) Como eu iria fazer isso? Idealmente, eu gostaria de ser capaz de dizer que faixa de 10-20 é igual a "Foo" e tê-lo ser a string "Foo" com 7 caracteres em branco adicionais (supondo que o referido campo tem um comprimento de 10) e tem que ser um parte do campo de 80 caracteres maior, mas não tenho certeza de como fazer o que eu estou pensando.
Solução
Você não precisa atribuir a fatias, apenas construir a string usando % formatting
.
Um exemplo com um formato fixo para 3 itens de dados:
>>> fmt="%4s%10s%10s"
>>> fmt % (1,"ONE",2)
' 1 ONE 2'
>>>
A mesma coisa, largura do campo fornecido com os dados:
>>> fmt2 = "%*s%*s%*s"
>>> fmt2 % (4,1, 10,"ONE", 10,2)
' 1 ONE 2'
>>>
Separar dados e larguras de campo, e usando zip()
e str.join()
truques:
>>> widths=(4,10,10)
>>> items=(1,"ONE",2)
>>> "".join("%*s" % i for i in zip(widths, items))
' 1 ONE 2'
>>>
Outras dicas
Espero que eu entendo o que você está procurando:? Alguma maneira de identificar convenientemente cada parte da linha por uma variável simples, mas de saída lo preenchido para a largura correta ??p>
O trecho a seguir podem lhe dar o que você quer
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
Este rendimentos:
hi joe howya doing
Ele funciona armazenando uma variável de nível de classe, fields
que registra a ordem em que cada campo deve aparecer na saída, juntamente com o número de colunas que o campo deve ter. Não são correspondentemente-nomeado variáveis ??de instância na __init__
que estão definidas para uma cadeia vazia inicialmente.
O método __str__
gera estes valores como uma string. Ele usa uma compreensão lista sobre o atributo fields
em nível de classe, olhando para cima o valor de instância para cada campo pelo nome, e depois deixou-justificando-a de saída de acordo com as colunas. A lista resultante de campos é, então, unidos por uma cadeia vazia.
Note que este não faz entrada de análise, embora você poderia facilmente substituir o construtor para tomar uma string e analisar as colunas de acordo com as larguras de campo e de campo em fields
. Ele também não verifica valores de instância que são mais longos do que a sua largura alocado.
Você pode usar justificar funções para a esquerda-justificar, direita justificar e centralizar uma string em um campo de determinada largura.
'hi'.ljust(10) -> 'hi '
Eu sei que esta discussão é bastante antigo, mas usamos uma biblioteca chamada django-copybook . Não tem nada a ver com Django (mais). Vamos utilizá-lo para ir entre os arquivos COBOL largura fixa e python. Você cria uma classe para definir o layout do registro largura fixa e pode fácil mover entre objetos python digitado e largura fixa arquivos:
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)
Ele também pode lidar com situações semelhantes a Cobol do OCORRE funcionalidade como quando uma seção particular é repetido vezes X
É um pouco difícil de analisar sua pergunta, mas eu estou reunindo que você está recebendo um arquivo ou arquivo-como-objeto, lê-lo, e substituindo alguns dos valores com alguns resultados de lógica de negócios. É este correto?
A maneira mais simples para superar imutabilidade corda é escrever uma nova string:
# Won't work:
test_string[3:6] = "foo"
# Will work:
test_string = test_string[:3] + "foo" + test_string[6:]
Dito isto, parece que ele é importante para você que você fazer algo com esta cadeia, mas eu não sei exatamente o que é. Você está escrevendo-lo de volta para um arquivo de saída, tentando editar um arquivo no lugar, ou algo mais? Eu trago este acima porque o ato de criar uma nova string (que passa a ter o mesmo nome da variável como a cadeia de idade) deve enfatizar a necessidade de realizar uma operação de escrita explícita após a transformação.
Você pode converter a string para uma lista e fazer a manipulação fatia.
>>> 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'
É fácil função de gravação para "modificar" string.
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:]
Uso:
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
Eu usei o exemplo de Jarret Hardie e modificou-o ligeiramente. Isto permite a seleção do tipo de alinhamento de texto (esquerda, direita ou centralizado.)
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
Retorna:
Alex Programmer Salem, OR
Alex Programmer Salem, OR