Tkinter GUI将固定宽度文件转换为分隔文件

时间:2012-01-12 17:14:12

标签: python file tkinter fixed-width delimited

我正在为我们的数据部门编写转换器代码,以将固定宽度文件转换为delmited文件。通常我们使用将文件导入Excel,使用文本导入向导设置字段长度,然后只保存为csv。但是,我们遇到了限制,我们已经开始获取数百万条记录的文件,因此无法导入Excel。这些文件在字段之间并不总是有空格,特别是在电话号码或邮政编码等值字段之间。标题也经常填充,没有空格。

我们正在处理的典型固定宽度文件的示例:

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

程序需要将文件分成以下分隔字段:

Sequen
Sack和Pa
全名
职称
医院名称
送货地址
备用地址1
计算文字
POSTNET栏

每个文件的宽度略有不同,具体取决于作业的其余部分。我正在寻找的是面向GUI的分隔符,就像固定宽度文件的Excel导入向导一样。我在Python中编写这个工具,作为一个更大工具的一部分,它可以执行许多其他文件操作,例如将文件拆分为多个文件,反转文件,从分隔符转换为固定宽度以及检查数字检查。我正在使用Tkinter来使用其他工具,如果解决方案也使用它,那将是理想的。

任何帮助表示赞赏

2 个答案:

答案 0 :(得分:3)

如果我理解正确的问题(并且我很有可能......),最简单的解决方案可能是使用文本小部件。

使第一行成为与行长度相同的一系列空格。使用几个交替的标签(例如:"偶数"和#34;奇数")为每个角色提供另一种颜色,使它们彼此脱颖而出。第二行是标题,任何剩余的行都是几行样本数据。

然后,在第一行设置绑定,将空格转换为" x"当用户点击一个角色时。如果他们点击" x",请将其转换回空格。然后,他们可以单击作为每列开头的字符。当用户完成后,您可以获得文本小部件的第一行,它将具有" x"对于每一列。然后,您只需要一个小功能,将其转换为您需要的任何格式。

它看起来大致相似(虽然显然颜色会与本网站上显示的颜色不同)

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

这里有一个快速的黑客来说明一般的想法。它有点草率但我认为它说明了这种技术。运行时,单击第一行中的某个区域以设置或清除标记。这将导致标题以每种标记的替代颜色突出显示。

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

答案 1 :(得分:0)

编辑:我现在看到你正在寻找一个gui。我会给后人留下这个错误的答案。

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)