tkinter实现通用对账文件解析软件

软件需求

和银行等金融机构合作过程中,经常会有还款计划、放款文件等定时推送的文件,以常见的分隔符进行分隔开,为了在系统出现问题时,快速查找异常数据,写了一个小工具解析这些文件并写入到excel中。

image-20240403171558023

软件功能

将常见分隔符的文件解析后写入到excel中

软件界面

image-20240403170655979

软件源码

  • 使用前请安装openpyxl,pip install openpyxl,可使用pyinstaller打包成exe文件。
import os
import datetime
import logging

from openpyxl import Workbook

import tkinter as tk
from tkinter import filedialog,messagebox
from tkinter.ttk import *



class FileParser(object):

    def __init__(self):
        pass

    @staticmethod
    def getAllFiles(folder,data_str,suffix=".txt"):
        '''获取文件路径下的所有文件'''
        filepath_list = []
        for root,folder_names, file_names in os.walk(folder):
            for file_name in file_names:
                file_path = root + os.sep + file_name
                if data_str != '' or data_str is not None:
                    if data_str in file_name and os.path.splitext(file_path)[-1]==suffix:
                        filepath_list.append(file_path)
                else:
                    if os.path.splitext(file_path)[-1]==suffix:
                        filepath_list.append(file_path)
        if len(filepath_list)==0:
            messagebox.showinfo('文件不存在',"该目录下不存在包含{}且后缀为{}的文件,请检查!".format(data_str,suffix))
            return
        file_path = sorted(file_path, key=str.lower)
        return filepath_list


    def batchParseDataFile(self,data_str,logbox,start_date,end_date,date_format,source_dir,target_dir,source_suffix=".txt",data_sep="|",data_start_row=1,data_end_flag="",start_row=1,start_col=1):
        '''
        获取数据文件中的有效数据
        :param start_date:解析的文件的开始日期
        :param end_date:解析的结束日期
        :param date_format:日期格式
        :param source_dir:需要解析的文件的文件夹路径
        :param target_dir:生成excel文件的文件夹路径
        :param data_sep:数据分隔符
        :param data_start_row:有效数据的开始行
        :param data_end_flag:有效数据结束标识
        :param start_row:写入excel的开始行
        :param start_col:写入excel的开始列
        :param table_head:有效数据是否包含表头
        '''
        self.log = logbox

        start=datetime.datetime.strptime(start_date,date_format)
        end=datetime.datetime.strptime(end_date,date_format)
        source_filelist = FileParser.getAllFiles(source_dir,data_str,source_suffix)
        if source_filelist is None or len(source_filelist)==0:
            self.log.info("该目录{}下不存在文件".format(source_dir))
            return
        if start > end:
            self.log.info("开始日期{}大于结束日期{}".format(start_date,end_date))
            return
        files = []
        null_files = []
        starttime = datetime.datetime.now()
        while start<=end:

            for fname in source_filelist:
                if start.strftime(date_format) in fname:
                    if os.stat(fname).st_size==0:
                        null_files.append(fname)
                    else:
                        files.append(fname)
                        basename = os.path.basename(fname)
                        file_date = start.strftime(date_format)

                        self.log.info("{}日期的{}文件正在解析".format(file_date,basename))
                        target_basename =  basename.split(".")[0]+".xlsx" if file_date in basename else basename.split(".")[0]+"_"+file_date+".xlsx"

                        target_fname = os.path.join(target_dir,target_basename)
                        self.log.info("生成{}日期的excel文件{}".format(file_date,target_basename))
                        self.parseDataFile(fname,target_fname,data_sep=data_sep,data_start_row=data_start_row,data_end_flag=data_end_flag,start_row=start_row,start_col=start_col)
            start+=datetime.timedelta(days=1)
        endtime=datetime.datetime.now()
        costtime = round((endtime - starttime).total_seconds(),2)
        if len(files)==0:
            self.log.info("{}目录不存在{}-{}时间区间内的目标文件".format(source_dir,start_date,end_date))
        else:
            messagebox.showinfo('解析完成提示',"文件解析完成,{}-{}时间段内合计{}个文件,空文件{}个,解析{}个有数据文件,,耗时{}秒".format(start_date,end_date,len(files)+len(null_files),len(null_files),len(files),costtime))




    def parseDataFile(self,source_dir,target_dir,data_sep="|",data_start_row=1,data_end_flag="",start_row=1,start_col=1):
        '''
        获取数据文件中的有效数据
        :param source_dir:数据文件绝对路径
        :param target_dir:生成excel文件的绝对路径
        :param data_sep:数据分隔符
        :param data_start_row:有效数据的开始行
        :param data_end_flag:有效数据结束标识
        :param start_row:写入excel的开始行
        :param start_col:写入excel的开始列
        :param table_head:有效数据是否包含表头
        '''
        data = self.__getFileData(source_dir,data_sep,data_start_row,data_end_flag)
        fname,basename = self.__writeToExcel(source_dir,target_dir,data,start_row,start_col)
        return fname,basename



    def __getFileData(self,filename,data_sep,data_start_row,data_end_flag):
        data = []
        try:
            if os.stat(filename).st_size==0:
                self.log.info("{}文件为空".format(os.path.basename(filename)))
            with open(filename,"r",encoding="utf-8") as f:
                line_index = 1
                for line in f:
                    if line:
                        line = line.replace("\n","")
                        if line_index >= data_start_row and line != data_end_flag:
                            data.append(line.split(data_sep))
                        if line == data_end_flag:
                            break
                    else:
                        break
                    line_index = line_index+1
        except Exception as e:
            self.log.info("{}解析错误".format(filename))
        return data


    def __writeToExcel(self,source_file,target_file,data,start_row,start_col):
        basename =None
        try: 
            if os.stat(source_file).st_size==0 or data is None or len(data)==0:
                self.log.info("{}文件为空,未生成对应的excel文件".format(source_file))
            else:
                with open(target_file,"w+",encoding="utf-8") as f:
                    pass
                basename = os.path.basename(target_file)
                # self.__write_col(filename,basename,col=start_col,data=data)
                self.__write2ExcelOpenexl(target_file,basename,row=start_row,col=start_col,data=data)
                self.log.info("数据写入excel完成,文件路径{}".format(target_file))
        except Exception as e:
            self.log.info("写入文件{}错误".format(e))
        return target_file,basename



    def __write2ExcelOpenexl(self,excelname,sheet,row,col,data=None):
        wb = Workbook()
        ws = wb.create_sheet(sheet, 0)
        for y,i in enumerate(data):
            for x,n in enumerate(i):
                ws.cell( y + col, x + row, n )
        wb.save(excelname)
        wb.close()


class LoggerBox(tk.Text):

    def write(self, message):
        self.insert("end", message)


class APP(object):

    def __init__(self,window):
        window.title('通用对账文件解析软件')

        #获取屏幕尺寸计算参数,使窗口显示再屏幕中央
        screen_width = window.winfo_screenwidth() 
        screen_height = window.winfo_screenheight()
        width = 750
        height = 800
        # window_size = '%dx%d+%d+%d' % (width, height, (screen_width-width)/2, (screen_height-height)/2)
        window_size = f'{width}x{height}+{round((screen_width-width)/2)}+{round((screen_height-height)/2)}'
        window.geometry(window_size)

        z = tk.Label(window,anchor = 'center',bg='white',justify = 'center', width=500, height=500)
        z.place(x = 0, y = 0)

        a = tk.Label(window,anchor = 'center',text="对账文件解析软件",relief="raised", font=('TimesNewRoman', 30),justify = 'center', width=32, height=1)
        a.place(x = 50, y = 30)


        self.source_dir = tk.StringVar()
        self.source_dir.set("数据文件路径")
        b = tk.Label(window,anchor='nw',textvariable=self.source_dir,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        b.place(x = 50, y = 100)

        self.data_str = tk.StringVar()
        self.data_str.set("文件包含字符串")
        c = tk.Label(window,anchor='nw',textvariable=self.data_str,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        c.place(x = 50, y = 150)


        self.suffix = tk.StringVar()
        self.suffix.set("数据文件后缀")
        d = tk.Label(window,anchor='nw',textvariable=self.suffix,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        d.place(x = 400, y = 150)


        self.target_dir = tk.StringVar()
        self.target_dir.set("文件存放路径")
        f = tk.Label(window,anchor='nw',textvariable=self.target_dir,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        f.place(x = 50, y = 200)

        self.data_sep = tk.StringVar()
        self.data_sep.set("数据分隔符")
        g = tk.Label(window,anchor='nw',textvariable=self.data_sep,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        g.place(x = 50, y = 250)

        self.start_date = tk.StringVar()
        self.start_date.set("开始日期")
        h = tk.Label(window,anchor='nw',textvariable=self.start_date,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        h.place(x = 50, y = 300)

        self.end_date = tk.StringVar()
        self.end_date.set("结束日期")
        i = tk.Label(window,anchor='nw',textvariable=self.end_date,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        i.place(x = 400, y = 300)

        self.date_format = tk.StringVar()
        self.date_format.set("日期格式")
        j = tk.Label(window,anchor='nw',textvariable=self.date_format,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        j.place(x = 50, y = 350)


        self.data_start_row = tk.StringVar()
        self.data_start_row.set("数据开始行")
        k = tk.Label(window,anchor='nw',textvariable=self.data_start_row,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        k.place(x = 50, y = 400)

        self.data_end_flag = tk.StringVar()
        self.data_end_flag.set("数据结束标志")
        l = tk.Label(window,anchor='nw',textvariable=self.data_end_flag,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        l.place(x = 50, y = 450)

        self.start_row = tk.StringVar()
        self.start_row.set("excel写入行")
        m = tk.Label(window,anchor='nw',textvariable=self.start_row,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        m.place(x = 50, y = 500)

        self.start_col = tk.StringVar()
        self.start_col.set("excel写入列")
        n = tk.Label(window,anchor='nw',textvariable=self.start_col,fg='black',bg='white',font=('TimesNewRoman',12),justify='center',width=50,height=1)
        n.place(x = 400, y = 500)


        self.source_dir = tk.StringVar()
        entry_source_dir = tk.Entry(window, textvariable=self.source_dir).place(x=200, y=100)
        tk.Button(window,text="选择源文件夹",command=lambda:self.select_dir(self.source_dir),width=15,height=1).place(x=400, y=100)

        self.data_str = tk.StringVar()
        entry_source_dir = tk.Entry(window, textvariable=self.data_str).place(x=200, y=150)


        self.suffix = tk.StringVar()
        self.suffix.set(".txt")
        entry_source_dir = tk.Entry(window, textvariable=self.suffix).place(x=550, y=150)

        self.target_dir = tk.StringVar()
        entry_target_dir = tk.Entry(window, textvariable=self.target_dir).place(x=200, y=200)
        tk.Button(window,text="选择存放文件夹",command=lambda:self.select_dir(self.target_dir),width=15,height=1).place(x=400, y=200)


        self.data_sep = tk.StringVar()
        self.data_sep.set("|")
        entry_data_sep = tk.Entry(window, textvariable=self.data_sep).place(x=200, y=250)



        self.date_format = tk.StringVar()
        self.date_format.set("%Y%m%d")
        entry_date_format = tk.Entry(window, textvariable=self.date_format).place(x=200, y=350)


        now=datetime.datetime.now()
        self.start_date = tk.StringVar()
        self.start_date.set(now.strftime(self.date_format.get()))
        entry_start_date = tk.Entry(window, textvariable=self.start_date).place(x=200, y=300)

        self.end_date = tk.StringVar()
        self.end_date.set(now.strftime(self.date_format.get()))
        entry_end_date = tk.Entry(window, textvariable=self.end_date).place(x=550, y=300)



        self.data_start_row = tk.IntVar()
        self.data_start_row.set(int(1))
        entry_data_start_row = tk.Entry(window, textvariable=self.data_start_row).place(x=200, y=400)

        self.data_end_flag = tk.StringVar()
        entry_data_end_flag = tk.Entry(window, textvariable=self.data_end_flag).place(x=200, y=450)


        self.start_row = tk.IntVar()
        self.start_row.set(int(1))
        entry_start_row = tk.Entry(window, textvariable=self.start_row).place(x=200, y=500)

        self.start_col = tk.IntVar()
        self.start_col.set(int(1))
        entry_start_col = tk.Entry(window, textvariable=self.start_col).place(x=550, y=500)


        n = tk.Button(window, text='开始解析', width=90,height=1, command=self.parse_file)
        n.place(x = 50,y = 550)


        logBox = LoggerBox(window, width=90,height=10)
        logBox.place(x=50,y=600)
        self.log = logging.getLogger("log")
        self.log.setLevel(logging.INFO)
        handler = logging.StreamHandler(logBox)
        self.log.addHandler(handler)




    def select_dir(self,dir):
        dir.set(filedialog.askdirectory())



    def parse_file(self):
        try:
            parser = FileParser()
            parser.batchParseDataFile(self.data_str.get(),self.log,self.start_date.get(),self.end_date.get(),self.date_format.get(),self.source_dir.get(),self.target_dir.get(),self.suffix.get(),self.data_sep.get(),self.data_start_row.get(),self.data_end_flag.get(),self.start_row.get(),self.start_col.get())
        except  Exception as e:
            messagebox.showinfo('错误信息',e)



if __name__=="__main__":

    window = tk.Tk()
    app = APP(window)
    window.mainloop()

本文由【产品经理不是经理】gzh 同步发布,欢迎关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513637.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue快速入门(四)v-html

注释很详细&#xff0c;直接上代码 上一篇 新增内容 使用v-html将文本以html的方式显示 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, …

SSM框架整合

第一章&#xff1a;搭建整合环境 1. 搭建整合环境 1. 整合说明&#xff1a; SSM整合可以使用多种方式&#xff0c;咱们会选择XML 注解的方式。 2. 整合的思路 1. 先搭建整合的环境 2. 先把Spring的配置搭建完成 3. 再使用Spring整合SpringMVC框架 4. 最后使用Spring整…

全面的Docker快速入门教程(详细)

前言&#xff1a; 都2024年了&#xff0c;你还在为了安装一个开发或者部署环境、软件而花费半天的时间吗&#xff1f;你还在解决开发环境能够正常访问&#xff0c;而发布测试环境无法正常访问的问题吗&#xff1f;你还在为持续集成和持续交付&#xff08;CI / CD&#xff09;工…

Rust所有权和Move关键字使用和含义讲解,以及Arc和Mutex使用

Rust 所有权规则 一个值只能被一个变量所拥有&#xff0c;这个变量被称为所有者。 一个值同一时刻只能有一个所有者&#xff0c;也就是说不能有两个变量拥有相同的值。所以对应变量赋值、参数传递、函数返回等行为&#xff0c;旧的所有者会把值的所有权转移给新的所有者&#…

标题:巨控GRM560:医药行业设备管理的预警的革新者

描述&#xff1a;在快速发展的医药行业中&#xff0c;设备的稳定性和安全性至关重要。巨控GRM560模块作为一种前沿的数据采集和故障提醒系统&#xff0c;已经成为该行业内不可或缺的一部分。本文将深入探讨GRM560模块的功能及其在医药行业中的应用。 正文&#xff1a; 在当今…

Leetcode刷题-数组(二分法、双指针法、窗口滑动)

数组 1、二分法 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 需要注意区间的问题。首先在最外面的循环判断条件是left<right。那就说明我们区间规定的范围就是【left,right】 属于是左闭右闭&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&…

I2C总线与AT24C02

目录 I2C总线 I2C总线介绍 I2C电路规范 I2C时序结构 起始与终止 代码理解 发送字节 代码理解 接收字节 代码理解 数据应答 代码理解 I2C的数据据帧 发送数据帧 接收数据帧 发送接收数据帧 AT24C02芯片 AT24C02介绍 引脚及应用电路 内部结构图 AT24C02数据…

Android 高德地图

1.获取Key 进入高德开放平台控制台&#xff0c;创建一个新应用。在创建的应用上点击"添加key"按钮&#xff0c;在弹出的对话框中&#xff0c;依次输入key名称&#xff0c;选择服务平台为“Android平台”&#xff0c;输入发布版安全码 SHA1、以及 Package。 获取 S…

贵州省NPP净初级生产力数据/NDVI数据

数据福利是专门为关注小编博客及公众号的朋友定制的&#xff0c;未关注用户不享受免费共享服务&#xff0c;已经被列入黑名单的用户和单位不享受免费共享服务。参与本号发起的数据众筹&#xff0c;向本号捐赠过硬盘以及多次转发、评论的朋友优先享有免费共享服务。 净初级生产…

Vue3从入门到实战:路由的query和params参数

在Vue 3中&#xff0c;我们可以通过路由的查询参数来传递数据。这意味着我们可以在不同的页面之间传递一些信息&#xff0c;以便页面可以根据这些信息来显示不同的内容或执行不同的操作。 查询参数的使用方式类似于在URL中添加附加信息&#xff0c;以便页面之间可以根据这些信息…

基于springboot+vue实现的酒店客房管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

Redis高可用主从复制与哨兵模式

前言 在生产环境中&#xff0c;除了采用持久化方式实现 Redis 的高可用性&#xff0c;还可以采用主从复制、哨兵模式和 Cluster 集群的方法确保数据的持久性和可靠性。 目录 一、主从复制 1. 概述 2. 作用 3. 主从复制流程 4. 部署 4.1 安装 redis 4.2 编辑 master 节…

Xxxxxx

数据库 1&#xff0c;B树与B树区别 1&#xff0c;B树每个节点存ID与其他数据字段&#xff0c;B非叶子结点&#xff0c;只存ID&#xff0c;叶子结点存完整数据 好处&#xff1a;每个层级B树&#xff0c;可以存储更多的额数据&#xff0c;层级更少&#xff0c;更扁平&#xff…

代码块的理解

如果成员变量想要初始化的值不是一个硬编码的常量值&#xff0c;而是需要通过复杂的计算或读取文件、或读取运行环境信息等方式才能获取的一些值&#xff0c;该怎么办呢&#xff1f;此时&#xff0c;可以考虑代码块&#xff08;或初始化块&#xff09;。 代码块(或初始化块)的作…

【Linux】IO多路转接

文章目录 一、selectselect函数select基本工作流程select的优缺点select的适用场景 二、pollpoll函数poll的优缺点 三、epollepoll相关系统调用epoll工作原理epoll的优点epoll工作方式对比LT和ET 一、select select是系统提供的一个多路转接接口。 select系统调用可以让我们的…

(echarts)vue中循环生成多个相同的echarts图表,但数据动态、第一次渲染失败问题

(echarts)vue中循环生成多个相同的echarts图表&#xff0c;但数据动态 效果&#xff1a; 代码&#xff1a; <!-- 动态图表 --> <el-row :gutter"20"><el-col v-for"(item,index) in echartsList" :key"index" :span"10&quo…

wordpress课程项目主题电脑版+手机版自适应

这款主题适合做资源、课程、素材等&#xff0c;演示站&#xff1a;点击查看

openwrt开发包含路由器基本功能的web问题记录

1.这里的扫描怎么实现的先找一些luci代码&#xff0c;在openwrt21版本后&#xff0c;luci用js替换了lua写后台&#xff0c;先找一些代码路径 在openrwt15这部分代码是在这个目录下 feeds/luci/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_join.htm 里面包含…

javaWeb旅游网站设计

一、概述 1.1 项目研究背景 社会经济的发展和提高潜移默化的影响了人们对精神消费的日益看中与提高&#xff0c;所以越来越多的人们开始选择更健康有趣的生活活动&#xff0c;随之而来的旅游便成了人们消费的必选。随着旅客需求的日趋丰富和个性化&#xff0c;这势必将推动我…

理解main方法的语法

由于JVM需要调用类的main()方法&#xff0c;所以该方法的访问权限必须是public&#xff0c;又因为JVM在执行main()方法时不必创建对象&#xff0c;所以该方法必须是static的&#xff0c;该方法接收一个String类型的数组参数&#xff0c;该数组中保存执行Java命令时传递给所运行…