Pregunta

Estoy escribiendo un código de convertidor para que nuestro departamento de datos convierta archivos de ancho fijos en archivos DelMited. Normalmente usamos importar el archivo en Excel, usamos el Asistente de importación de texto para establecer las longitudes de campo y luego guardamos como CSV. Sin embargo, nos hemos encontrado con la limitación en la que hemos comenzado a obtener archivos que tienen millones de registros y, por lo tanto, no se pueden importar a Excel. Los archivos no siempre tienen espacios entre los campos, especialmente entre los campos de valor como los números de teléfono o los códigos postales. Los encabezados también a menudo se llenan completamente sin espacios.

Una muestra de un archivo de ancho fijo típico con el que estamos tratando:

SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar
000001T1  P1     Sample A Sample                                                                                         123 Any Street                                                                  Anytown 12345-6789                                12345678900
000002T1  P1     Sample A Sample                       Director of Medicine                                              123 Any Street                          Po Box 1234                             Anytown 12345-6789                                12345678900

El programa necesita dividir el archivo en los siguientes campos delimitados:

Secuencia
Sack y PA
Nombre completo
Título profesional
Nombre de hospital
Dirección de entrega
Dirección alternativa 1
Texto calculado
Barra postnet

Cada archivo como un ancho ligeramente diferente de cada campo depende del resto del trabajo. Lo que estoy buscando es un delimitador orientado a la GUI, muy parecido al Asistente de importación de Excel para archivos de ancho fijo. Estoy escribiendo esta herramienta en Python como parte de una herramienta más grande que realiza muchas otras operaciones de archivos, como romper los archivos en múltiples UP, revertir un archivo, convertir de delimitado a ancho fijo y verificar la verificación de dígitos. Estoy usando Tkinter para el resto de las herramientas y sería ideal si la solución también lo usa.

Cualquier ayuda apreciada

¿Fue útil?

Solución

Si entiendo el problema correctamente (y hay una buena posibilidad de que no ...), la solución más simple podría ser usar un widget de texto.

Haga que la primera línea sea una serie de espacios de la misma longitud que la fila. Use un par de etiquetas alternativas (por ejemplo: "incluso" y "impar") para darle a cada personaje un color alternativo para que se destaquen entre sí. La segunda línea sería el encabezado, y cualquier línea restante sería un par de líneas de datos de muestra.

Luego, configure los enlaces en la primera fila para convertir un espacio en una "X" cuando el usuario hace clic en un personaje. Si hacen clic en una "X", conviértalo de nuevo a un espacio. Luego pueden ir y hacer clic en el personaje que es el comienzo de cada columna. Cuando termine el usuario, puede obtener la primera línea del widget de texto y tendrá una "X" para cada columna. Luego solo necesita una pequeña función que lo traduzca en cualquier formato que necesite.

Se vería más o menos así (aunque obviamente los colores serían diferentes de lo que aparece en este sitio web)

      x          x                                     x  ...
SequenSack and PaFull Name****************************]JOB...
000001T1  P1     Sample A Sample                          ...

Aquí hay un truco rápido para ilustrar la idea general. Es un poco descuidado, pero creo que ilustra la técnica. Cuando lo ejecute, haga clic en un área en la primera fila para configurar o borrar un marcador. Esto hará que el encabezado se resalte en colores alternativos para cada marcador.

import sys
import Tkinter as tk
import tkFont

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        header = "SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar"
        sample = "000001T1  P1     Sample A Sample                                                                                         123 Any Street                                                                  Anytown 12345-6789                                12345678900"
        widget = DelimiterWidget(self, header, sample)
        hsb = tk.Scrollbar(orient="horizontal", command=widget.xview)
        widget.configure(xscrollcommand=hsb.set)
        hsb.pack(side="bottom", fill="x")
        widget.pack(side="top", fill="x")

class DelimiterWidget(tk.Text):
    def __init__(self, parent, header, samplerow):
        fixedFont = tkFont.nametofont("TkFixedFont")
        tk.Text.__init__(self, parent, wrap="none", height=3, font=fixedFont)
        self.configure(cursor="left_ptr")
        self.tag_configure("header", background="gray")
        self.tag_configure("even", background="#ffffff")
        self.tag_configure("header_even", background="bisque")
        self.tag_configure("header_odd", background="lightblue")
        self.tag_configure("odd", background="#eeeeee")
        markers = " "*len(header)
        for i in range(len(header)):
            tag = "even" if i%2==0 else "odd"
            self.insert("end", " ", (tag,))
        self.insert("end", "\n")
        self.insert("end", header+"\n", "header")
        self.insert("end", samplerow, "sample")
        self.configure(state="disabled")
        self.bind("<1>", self.on_click)
        self.bind("<Double-1>", self.on_click)
        self.bind("<Triple-1>", self.on_click)

    def on_click(self, event):
        '''Handle a click on a marker'''
        index = self.index("@%s,%s" % (event.x, event.y))
        current = self.get(index)
        self.configure(state="normal")
        self.delete(index)
        (line, column) = index.split(".")
        tag = "even" if int(column)%2 == 0 else "odd"
        char = " " if current == "x" else "x"
        self.insert(index, char, tag)
        self.configure(state="disabled")
        self.highlight_header()
        return "break"

    def highlight_header(self):
        '''Highlight the header based on marker positions'''
        self.tag_remove("header_even", 1.0, "end")
        self.tag_remove("header_odd", 1.0, "end")
        markers = self.get(1.0, "1.0 lineend")

        i = 0
        start = "2.0"
        tag = "header_even"
        while True:
            try:
                i = markers.index("x", i+1)
                end = "2.%s" % i
                self.tag_add(tag, start, end)
                start = self.index(end)
                tag = "header_even" if tag == "header_odd" else "header_odd"
            except ValueError:
                break

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

Otros consejos

editar: Ahora veo que estás buscando una GUI. Dejaré esta respuesta incorrecta para la posteridad.

import csv

def fixedwidth2csv(fw_name, csv_name, field_info, headings=None):
    with open(fw_name, 'r') as fw_in:
        with open(csv_name, 'rb') as csv_out: # 'rb' => 'r' for python 3
            wtr = csv.writer(csv_out)
            if headings:
                wtr.writerow(headings)
            for line in fw_in:
                wtr.writerow(line[pos:pos+width].strip() for pos, width in field_info)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top