2009-05-11 43 views
11

Soy un novato en Python y estoy buscando usarlo para escribir algunas cosas peludas de EDI que nuestro proveedor requiere.Escribiendo/analizando un archivo de ancho fijo usando Python

Básicamente necesitan un archivo de texto de ancho fijo de 80 caracteres, con ciertos "fragmentos" del campo con datos y otros en blanco. Tengo la documentación, así que sé cuál es la duración de cada "porción". La respuesta que obtengo es más fácil de analizar porque ya tendrá datos y puedo usar las "divisiones" de Python para extraer lo que necesito, pero no puedo asignar a un segmento. Ya lo intenté porque sonaba como un buen solución, y no funcionó ya que las cadenas de Python son inmutables :)

Como dije, soy realmente novato en Python pero estoy emocionado de aprenderlo :) ¿Cómo podría hacer esto? Idealmente, me gustaría poder decir que el rango 10-20 es igual a "Foo" y que sea la cadena "Foo" con 7 caracteres de espacio en blanco adicionales (suponiendo que dicho campo tenga una longitud de 10) y que sea un parte del campo más grande de 80 caracteres, pero no estoy seguro de cómo hacer lo que estoy pensando.

+0

¿Está procesando mensajes X12 EDI? El diseño no es realmente fijo. ¿Estás procesando algún otro formato? Si es así, no es realmente [EDI] ¿verdad? Solo se arregló el diseño del archivo. –

+0

No tengo idea, realmente. Se refieren a él como "EDI" en toda su documentación. Todo lo que sé es que tengo que enviarles un registro (lo llaman un registro "H0" y me enviarán un archivo para analizar. –

+0

El encabezado ISA de X12 es de ancho fijo (la primera línea) como los delimitadores no se declaran hasta el final de la línea. – charlesbridge

Respuesta

16

No es necesario que asigne secciones, solo construya la cadena usando % formatting.

Un ejemplo con un formato fijo para los artículos 3 de datos:

>>> fmt="%4s%10s%10s" 
>>> fmt % (1,"ONE",2) 
' 1  ONE   2' 
>>> 

Lo mismo, ancho de campo suministrada con los datos:

>>> fmt2 = "%*s%*s%*s" 
>>> fmt2 % (4,1, 10,"ONE", 10,2) 
' 1  ONE   2' 
>>> 

La separación de datos y anchos de campo, y el uso de zip() y str.join() trucos:

>>> widths=(4,10,10) 
>>> items=(1,"ONE",2) 
>>> "".join("%*s" % i for i in zip(widths, items)) 
' 1  ONE   2' 
>>> 
0

Es un poco difícil analizar su pregunta, pero estoy recopilando que está recibiendo un archivo o archivo-como-objeto, leyéndolo y reemplazando algunos de los valores con algunos resultados de lógica de negocios. ¿Es esto correcto?

La forma más sencilla de superar la inmutabilidad cadena es escribir una nueva cadena:

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

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

Una vez dicho esto, parece que es importante para usted que se hace algo con esta cadena, pero no estoy seguro exactamente lo que es eso ¿Lo está escribiendo nuevamente en un archivo de salida, tratando de editar un archivo en su lugar, o alguna otra cosa? Menciono esto porque el acto de crear una nueva cadena (que tiene el mismo nombre de variable que la anterior) debe enfatizar la necesidad de realizar una operación de escritura explícita después de la transformación.

7

Espero que entienda lo que eres e buscando: ¿alguna forma de identificar convenientemente cada parte de la línea por una variable simple, pero la salida se rellena con el ancho correcto?

El siguiente fragmento de código que puede darle lo que quiere

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 

Esto produce:

hi  joe       howya       doing  

Funciona mediante el almacenamiento de una variable de nivel de clase, fields que registra el orden en que cada campo debe aparecer en el resultado, junto con la cantidad de columnas que ese campo debería tener. Hay variables de instancia con nombre correspondiente en el __init__ que se configuran en una cadena vacía inicialmente.

El método __str__ muestra estos valores como una cadena. Utiliza una lista de comprensión sobre el atributo de nivel de clase fields, busca el valor de instancia para cada campo por nombre y luego justifica a la izquierda su salida según las columnas. La lista resultante de campos se une luego por una cadena vacía.

Tenga en cuenta que esto no analiza la entrada, aunque puede anular fácilmente el constructor para tomar una cadena y analizar las columnas de acuerdo con el campo y el ancho del campo en fields. Tampoco comprueba, por ejemplo, los valores que son más largos que su ancho asignado.

0

Puede convertir la cadena en una lista y realizar la manipulación de sectores.

>>> 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

Intersting. No es necesario convertirlo a una lista para extraer. Eso es una tontería. Pero construir una lista y luego colapsar en una cadena ... le da lo que parece un poco como una "cadena mutable", solo si asigna previamente suficiente espacio. –

+0

En realidad, no necesita preasignar nada si no le importa demasiado el rendimiento. El tipo de lista asignará automáticamente más espacio si el Rango cortado se le asigna un rango más grande. – Skurmedel

+0

También la conversión de la lista está ahí para mayor claridad. Por supuesto, podría ser mejor si él lea los datos directamente en una lista desde el principio, pero eso no es lo que quería mostrar. – Skurmedel

0

Es fácil escribir la función para "modificar" la cadena.

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 
7

Puede utilizar justify funciones a la izquierda justificar, justificar a la derecha y el centro de una cadena en un campo de anchura dada.

'hi'.ljust(10) -> 'hi  ' 
0

Utilicé el ejemplo de Jarret Hardie y lo modifiqué ligeramente. Esto permite la selección del tipo de alineación del texto (izquierda, derecha o centrado.)

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 

Devuelve:

Alex  Programmer   Salem, OR  
     Alex   Programmer  Salem, OR 
1

que conozco este hilo es bastante antiguo, pero utilizamos una biblioteca llamada django-copybook. No tiene nada que ver con django (más). Lo usamos para ir entre archivos cobol de ancho fijo y python. Se crea una clase para definir su diseño de registro ancho fijo y puede moverse con facilidad entre los objetos con tipo pitón y archivos de ancho fijo:

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) 

También puede manejar situaciones similares a Cobol de OCURRE funcionalidad como cuando una sección en particular se repite X veces

Cuestiones relacionadas