Domanda

Sto scrivendo un codice di convertitore per il nostro reparto dati per convertire i file di larghezza fissa in file delmitati. Normalmente utilizziamo l'importazione del file in Excel, utilizziamo la procedura guidata di importazione di testo per impostare le lunghezze del campo, quindi salvo come CSV. Tuttavia, abbiamo incontrato la limitazione in cui abbiamo iniziato a ottenere file che sono lunghi milioni di record e quindi non possiamo essere importati in Excel. I file non hanno sempre spazi tra i campi, in particolare tra campi di valore come numeri di telefono o codici postali. Anche le intestazioni sono spesso riempite completamente senza spazi.

Un campione di un tipico file di larghezza fissa con cui abbiamo a che fare:

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

Il programma deve interrompere il file nei seguenti campi delimitati:

Sequen
Sack e PA
Nome e cognome
Titolo di lavoro
Nome hosp
Indirizzo di consegna
Indirizzo alternativo 1
Testo calcolato
Postnet Bar

Ogni file come larghezza leggermente diversa di ciascun campo a seconda del resto del lavoro. Quello che sto cercando è un delimitatore orientato alla GUI molto simile alla procedura guidata di importazione Excel per i file di larghezza fissa. Sto scrivendo questo strumento in Python come parte di uno strumento più ampio che esegue molte altre operazioni di file come la rottura dei file in più UP, invertendo un file, convertendo da una larghezza delimitata in larghezza fissa e controlla il controllo della cifra. Sto usando Tkinter per il resto degli strumenti e sarebbe l'ideale se anche la soluzione lo usasse.

Qualsiasi aiuto apprezzato

È stato utile?

Soluzione

Se capisco correttamente il problema (e ci sono buone probabilità che non ...), la soluzione più semplice potrebbe essere quella di utilizzare un widget di testo.

Fai in modo che la prima riga sia una serie di spazi la stessa lunghezza della riga. Usa un paio di tag alternati (ad esempio: "anche" e "dispari") per dare a ogni personaggio un colore alternativo in modo che si distinguano l'uno dall'altro. La seconda riga sarebbe l'intestazione e tutte le linee rimanenti sarebbero un paio di linee di dati di esempio.

Quindi, impostare i binding nella prima riga per convertire uno spazio in una "X" quando l'utente fa clic su un carattere. Se fanno clic su una "X", convertilo in uno spazio. Possono quindi andare a fare clic sul personaggio che è l'inizio di ogni colonna. Quando l'utente è finito, è possibile ottenere la prima riga del widget di testo e avrà una "X" per ogni colonna. Quindi hai solo bisogno di una piccola funzione che lo traduce in qualsiasi formato di cui hai bisogno.

Sarebbe approssimativamente così (anche se ovviamente i colori sarebbero diversi da quelli che appare su questo sito)

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

Ecco un rapido hack per illustrare l'idea generale. È un po 'sciatto ma penso che illustri la tecnica. Quando lo esegui, fai clic su un'area nella prima riga per impostare o cancellare un marcatore. Ciò farà evidenziare l'intestazione in colori alternativi per ciascun marker.

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

Altri suggerimenti

modificare: Ora vedo che stai cercando una GUI. Lascerò questa risposta errata per i posteri.

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top