Tkinter GUI将定宽文件转换为分隔文件
我正在为我们的数据部门编写一个转换程序,目的是把固定宽度的文件转换成以分隔符分开的文件。通常,我们会把文件导入到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
这个程序需要把文件拆分成以下几个以分隔符分开的字段:
序列号
包裹和邮寄
全名
职位名称
医院名称
送货地址
备用地址1
计算文本
邮政编码条形码
每个文件中每个字段的宽度略有不同,这取决于具体的工作内容。我希望能有一个图形界面的分隔符设置工具,类似于Excel的导入向导,专门用于处理固定宽度的文件。我正在用Python编写这个工具,它是一个更大工具的一部分,这个大工具还可以进行其他文件操作,比如把文件拆分成多个文件、反转文件、从分隔符格式转换为固定宽度格式,以及校验数字检查。我在其他工具中使用Tkinter,如果这个解决方案也能使用Tkinter就更好了。
非常感谢任何帮助!
2 个回答
编辑:我现在明白你是在寻找一个图形用户界面(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)
如果我理解这个问题没错(其实我可能理解错了……),最简单的解决办法可能就是用一个文本小部件。
你可以让第一行是一些空格,长度和行一样。然后用几个交替的标签(比如“偶数”和“奇数”)给每个字符上不同的颜色,这样它们就能彼此区分开来。第二行可以是标题,剩下的几行可以是一些示例数据。
接着,你可以设置一些绑定,让用户点击第一行的字符时,把空格变成“x”。如果他们点击“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()