【Python】-表格拆分工具

设计一个相对通用型的表格拆分程序,并将其封装成exe格式

【核心设计思路】

1.使用pandas读取待拆分文件后,读取可用于拆分的字段名称
2.根据使用者传入的字段名称,对df进行分组
3.将各个df写入到表格中

【实际应用中需要解决的几个问题】

1.使用者传入的表格可能包含多个子表,因此需要使用者确认对哪个子表进行拆分操作;确认子表设置成可选择位置1.2.3或者输入子表名称的方式进行选取,因此此时还需要解决表格名称以数字命名的情形;
2.设置允许错误使用该程序的次数,达到允许错误产生的次数上限后强制退出程序;
3.若待拆分表格仅有1个子表,则无需进行子表选择操作.

    def choose_from_list(list_0,file_0,sht_type,positon,type=1):
    if type>0:
        list_1=[i.replace(file_0,'') for i in list_0]
    else:
        list_1=list_0[:]
    s=0
    t=0
    if len(list_0)==1:
        choosed_data=list_0[0]
    else:
        print('检测到【{}】内存在多个{},请选择其中一个进行后续操作:\n\t若{}是以整数命名的,请使用·[整数+]·的格式,如:{}+.'.format(positon,sht_type,sht_type,random.randint(1,9)))
        while s==0:
            if t<=3:
                print('可选{}清单:\n\t{}.'.format(sht_type,list_1))
                sht_x = input('请输入{}名称或{}位次:'.format(sht_type,sht_type))
                try:
                    sht_num=int(sht_x)
                    if sht_num<=1 or sht_num=='':
                        sht_num=0
                        choosed_data = list_0[sht_num]
                        s=1
                    else:
                        if sht_num>=len(list_0)+1:
                            print('\t※----下标越界-----------下标越界-----------下标越界----※\n')
                            t+=1
                        else:
                            sht_num-=1
                            s=1
                            choosed_data = list_0[sht_num]
                except (TypeError,ValueError):
                    if "'" in sht_x:
                        sht_x=sht_x.replace("'",'')
                    else:
                        if sht_x[-1]=='+':
                            sht_x=sht_x[:-1]
                        else:
                            pass
                    if sht_x not in list_1:
                        print('\t输入的{}名称不在【{}】内,请核验!\n'.format(sht_type,positon))
                        t+=1
                    else:
                        choosed_data=list_0[list_1 .index(sht_x)]
                        s=1
            else:
                s=1
                print('\n-----*-----您已多次输错{}名称,请核验后重启程序再次尝试!-----*-----\nHint:\n\t①注意审查输入的{}名称是否存在空格\n\t②直接在可选{}清单内复制对应名称'.format(sht_type ,sht_type,sht_type))
                choosed_data=''
    if len(choosed_data)>0:
        if type <=1:
            if len(list_0)>1:
                print('\t□······已选{}·[{}].\n'.format(sht_type,choosed_data.replace(file_0,'')))
            else:
                print('\t\t□······即将使用{}·[{}]的数据进行后续操作.\n'.format(sht_type, choosed_data.replace(file_0, '')))
        else:
            if len(list_0)>1:
                print('\t□······已选{}·[{}]作为样表写入数据.'.format(sht_type,choosed_data.replace(file_0,'')))
            else:
                print('\t□······将使用·[{}]·作为{}写入数据.'.format(choosed_data.replace(file_0, ''),sht_type))
        time.sleep(0.5)
    return choosed_data

4.将待拆分的表格放置在一个文件夹【待拆分总表】内,拆分的结果放置在【拆分结果】文件夹内,此外额外设置一个【空样表】文件夹,使得输出的表格按照指定的样式输出;因此此时需要完成以下几个认证工作:
4.1 文件夹的校验工作,即检验是否存在以上文件夹,如无进行自动创建
4.2 待拆分总表/样表的表格是否处于编辑状态验证,如处于编辑状态则需要提示使用者先对其关闭,并设置可在输入界面运行强制关闭的代码;

def check_data_source():
    f_x = [f_0 + i for i in os.listdir(f_0)]
    if len(f_x) == 0:
        print('\n※----※-----------源为空!-----------※----※')
        ori_data=''
    else:
        for i in f_x:  #剔除非xls和处于编辑状态的文件
            if '~$' in i:
                print('※----提示-----------数据源文件夹内的[{}]正处于编辑状态!-----------提示----※\n'.format((i.replace('~$', ''))).replace(f_0, ''))
                f_x.remove(i)
            else:
                if '.xls' not in i[-5:]:
                    print('※----提示-----数据源文件夹内的[{}]非本程序支持的.xls或.xlsx格式文件-----提示----※'.format(i.replace(f_0, '')))
                    f_x.remove(i)
                else:
                    pass
        ori_data=choose_from_list(f_x,f_0,'源表','待拆分总表',type=1)

    return ori_data

5.使用者引入的表格可能为多层级表头,或者表格前面几行为空,此时需要使用者确认使用的表头层级数量和跳空数量

def check_n_level(ori_data,sht_name):
	level,header=-1,-1
    d_0 = pd.read_excel(ori_data, header=0, sheet_name=sht_name, keep_default_na=False)
    q = 0
    while q == 0:
        try:
            level = int(input('【请输入表头层级】※-----------请输入表头层级数:'))
            k=0
            if level >= len(d_0):
                print('\t※----下标越界----------超越理论层级----------下标越界----※\n')
                header=[]
                level=''
            else:
                if level <0:
                    print('\t输入的格式或内容有误,请重新输入大于等于0的整数!\n')
                else:
                    if level in [1,0]:
                        level = 1
                    else:
                        pass
                    print('\t□······已锁定·[{}级]·表头.\n'.format(level))
                    header = [i for i in range(level)]
                    q = 1
        except (ValueError,TypeError):
            print('\t输入的格式或内容有误,请重新输入大于等于0的整数!\n')

    return level,header

6.优化可选项在端口的展示方式

def refix_col(col_0,step,diff=''):
    col_plus = int(len(col_0) % step)
    c = 1
    col_x = []
    for i in range(len(col_0)):
        if c % step == 0:
            print('\t{}{}'.format(diff,col_x))
            col_x = []
        else:
            col_x.append(col_0[i])
        c += 1
    col_left = col_0[-col_plus - 1:]
    if col_plus > 0:
        col_x = []
        for i in range(len(col_left)):
            col_x.append(col_left[i])
        print('\t{}{}'.format(diff,col_x))

7.询问使用者是否使用样表,并进行具体的样式验证

def example_xls_check(header):
    Use_Yb = 0
    p = 1
    yb_xls = ''
    yb_sht = ''
    p=0
    while p == 0:
        Use_Yb = input('\n【请输入内容】※-----------是否使用样表?(1/0): ')
        if Use_Yb in ['yes', '1', 'y', 'YES', 'Yes', '是', '']:
            Use_Yb=1
            print('\t□······已选[是]:将使用样表作为模板进行拆分写入.')
            # 判断样表是否符合规范
            f_z = [f_k + i for i in os.listdir(f_k)]
            if len(f_z) == 0:
                print('\t\t※----样表为空,请补充样表!-----※\n')
            else:
                yb_xls=choose_from_list(f_z,f_k,'样表','空样表',type=2)
                if '.xls' not in yb_xls[-5:]:
                    print('\n※----对象错误--------所使用的样表非.xls或.xlsx格式文件,请核验!-------对象错误----※')
                else:
                    yb_names = pd.ExcelFile(yb_xls).sheet_names
                    yb_sht=choose_from_list(yb_names,f_k,'样表_子表','已选样表',type=0)
                    try:
                        dz = pd.read_excel(yb_xls, header=header, sheet_name=yb_sht,keep_default_na=False) #
                        col_yb = list(dz.columns)
                        col_over=set(col_yb)-(set(col_yb)&set(col_0))
                        col_less=set(col_0)-(set(col_yb)&set(col_0))
                        if set(col_0) == set(col_yb):
                            p = 1
                            print('\t·············样表验证通过···············√')
                        else:
                            print('\t××××××××××表头不一致,样表验证未通过×××××××××××')
                            if len(col_over)>0:
                                print('\t错误类型:\n\t\t样表表头>待拆分表表头.')
                            else:
                                if len(col_less) > 0:
                                    print('\t错误类型:\n\t\t样表表头<待拆分表表头.')
                    except AttributeError:
                         print('\t※----※-------样表的格式设置不受支持!\n\t\t错误类型:\n\t\t\t可读取行数<=选定表头层级.\n\t\t请使用以下方法进行调试:\n\t\t\t①.新建空白表格,将表头复制至新表后设置格式\n\t\t\t②.使用已有表格作为样表的,请保证表头下方至少存在1个数据,如空格-------※----※')
        else:
            print('\t□······已选择[否]:将不参照样表格式直接进行拆分.')

    return Use_Yb,yb_xls,yb_sht

8.字段选择校验函数

def get_col_name(level,ori_data,sht_name):
    zd_name=''
    col_s = list(df.columns)
    wb = app.books.open(ori_data)
    sht = wb.sheets[sht_name]
    rng = sht.range('A{}'.format(level)).expand('table')
    c_0 = rng.columns.count
    wb.close()
    app.kill()
    col_0=col_s[:c_0]
    step = round(8 / level, 0) + 1
    print('·可选字段如下:')
    refix_col(col_0,step)
    p = 0
    s=0
    while p == 0:
        zd_name = input('\n【请输入内容】※-----多级表头请以··[列表]·或·(元组)··形式输入用于拆分的字段·[字段名称]:')
        if level==1:
            if zd_name[0]=="'" or zd_name[-1]=="'":
                zd_name=zd_name.replace("'",'')
            else:
                pass
        else:
            pass
        if zd_name !='zd_name':
            try:
                zd_name = tuple(eval(zd_name))
            except (NameError, SyntaxError, TypeError):
                pass
        if zd_name not in col_0:
            if ('\\n' in zd_name):  # or ('\t' in zd_name)
                print('\t※----对象错误---------不支持对含跨行符的非标准表头的拆分---------对象错误----※')
            else:
                s+=1
                if s<=2:
                    print('\t※----对象错误---------无效字段---------对象错误----※')
                else:
                    print('\t※----对象错误---------无效字段---------对象错误----※')
                    print('\tHint:请注意区分大小写及字段中可能存在的空格和标点符号.')
                try:
                    e = []
                    if level==1:
                        for j in col_0:
                            if zd_name in j:
                                e.append(j)
                    else:
                        for i in range(level):
                            for j in col_0:
                                if zd_name in j[i]:
                                    e.append(j)
                    e = list(set(e))
                    if len(e) == 0:
                        pass
                    else:
                        print('\t※····提示···包含该关键字的可选字段为:')
                        refix_col(e,step,diff='\t')
                except (NameError, SyntaxError, TypeError):
                    pass
        else:
            if len(list(set(list(df[zd_name])))) == 1:
                print('\t※-----不可拆分------该字段仅有1个可拆分序列,请选择其它可拆分字段-----不可拆分------※')
                pass
            else:
                print('\t··········字段校验通过···········√')
                p = 1
    return col_0,zd_name

9.给生成的各个子表设置一个统一的前方名称,注意某些字符不允许出现在excel文件名称内,需要逐个排除

def set_file_name():
    es = 0
    sp = 0
    s_name=''
    while es == 0:
        sk=0
        s_name = input('\n【请输入内容】※-----请输入即将应用至生成文件的规范文件名:')
        for ep in forbidden_symbol:
            if ep in s_name:
                sk+=1
        if sk>0:
            print('\t※----命名错误----键入的文件名不符合规范,请重新输入!----命名错误----※')
            sp+=1
            if sp>=2:
                print('\t提示:Excel文件名称不可包含以下字符:\n\t\t{}'.format(forbidden_symbol))
        else:
            es=1
            print('\t※-----文件名验证通过······√')
            time.sleep(1)
            print('\t······表格拆分开始······')

    if len(s_name)>0:
        s_name += '_'
    else:
        pass
    return s_name

10.写入样表的函数

def write_to_yb(df,level,zd_name,yb_xls,yb_sht,s_name):
    t_0 = time.time()
    app = xw.App(visible=False, add_book=False)
    app.display_alerts = False
    app.screen_updating = False
    target = list(set(list(df[zd_name])))
    for qs in tqdm.trange(len(target)):
        i = target[qs]
        time.sleep(0.25)
        ds = df.loc[df[zd_name] == i].copy()
        if str(i).strip() == '':
            i = '空白'
        else:
            for ep in forbidden_symbol:
                if ep in str(i):
                    i = eval(repr(str(i)).replace(ep, '@'))
                else:
                    i = str(i).strip()
        wb = app.books.open(yb_xls)
        sht = wb.sheets[yb_sht]
        sht.range('A{}'.format(level + 1)).options(transpose=False).value = ds.values.tolist()
        wb.save(f_s + s_name+str(i) + '.xlsx')
    app.kill()
    t_1 = time.time()
    print('\n √·······拆分完成!·······用时{}s,合计生成{}个子表·······※'.format(round(t_1 - t_0, 2), len(list(set(list(df[zd_name]))))))

11.写入空表的函数

def write_to_null(df,level,zd_name,s_name):
    t_0 = time.time()
    target = list(set(list(df[zd_name])))
    for qs in tqdm.trange(len(target)):
        i = target[qs]
        time.sleep(0.25)
        ds = df.loc[df[zd_name] == i].copy()
        if str(i).strip() == '':
            i = '空白'
        else:
            for ep in forbidden_symbol:
                if ep in str(i):
                    i = eval(repr(str(i)).replace(ep, '@'))
                else:
                    i = str(i).strip()
        if level == 1:
            ds.to_excel(f_s +s_name+ str(i) + '.xlsx', index=False)
        else:
            ex = app.books.add()
            st=ex.sheets[0]
            st.range('A1').options(transpose=True).value = ds.columns.tolist()  #.options(transpose=False)
            st.range('A{}'.format(level + 1)).options(transpose=False).value = ds.values.tolist()
            ex.save(f_s + s_name+str(i) + '.xlsx')
            ex.close()
    t_1 = time.time()
    print('\n √·······拆分完成!·······用时{}s,合计生成{}个子表·······※'.format(round(t_1 - t_0, 2), len(list(set(list(df[zd_name]))))))

12.将以上代码封装成exe格式后就可以给任意人员进行使用了,使用过程中会提供各种保姆式提示,按照提示进行逐步输入参数即可.

运行效果展示

在这里插入图片描述
在这里插入图片描述

程序存在的不足

1.读取较大的excel时会出现前期读取时间过长的问题,需要进一步优化
2.对非标准表头格式的支持一般

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一些实现这个小程序的思路。 1. 安装必要的库 要处理Excel文件,我们需要使用Python的第三方库`openpyxl`,所以首先需要安装该库。可以使用以下命令进行安装: ``` pip install openpyxl ``` 2. 选择Excel文件 您可以使用Python的`tkinter`库来创建一个GUI界面,以便用户选择要分的Excel文件。 以下是一个简单的示例代码: ```python import tkinter as tk from tkinter import filedialog root = tk.Tk() root.withdraw() file_path = filedialog.askopenfilename() ``` 3. 解析Excel文件 使用`openpyxl`库打开Excel文件,并将数据存储在Python中以便后续处理。以下是一个示例代码,假设Excel文件中只有一个工作表: ```python from openpyxl import load_workbook wb = load_workbook(filename=file_path) ws = wb.active # 将数据存储在Python中 data = [] for row in ws.iter_rows(values_only=True): data.append(row) ``` 4. 分Excel文件 根据用户的需求,您可以选择按行数或列数分Excel文件。 以下是按行数分Excel文件的示例代码: ```python # 按行数分Excel文件 row_num = 100 # 每个文件包含的行数 num_files = (len(data) + row_num - 1) // row_num # 计算需要分成的文件数 for i in range(num_files): start_row = i * row_num end_row = min(start_row + row_num, len(data)) file_name = f"{i + 1}.xlsx" wb = Workbook() ws = wb.active # 将数据写入Excel文件 for row in data[start_row:end_row]: ws.append(row) wb.save(file_name) ``` 以下是按列数分Excel文件的示例代码: ```python # 按列数分Excel文件 col_num = 10 # 每个文件包含的列数 num_files = (len(data[0]) + col_num - 1) // col_num # 计算需要分成的文件数 for i in range(num_files): start_col = i * col_num end_col = min(start_col + col_num, len(data[0])) file_name = f"{i + 1}.xlsx" wb = Workbook() ws = wb.active # 将数据写入Excel文件 for row in data: ws.append(row[start_col:end_col]) wb.save(file_name) ``` 5. 创建桌面小程序 您可以使用Python的第三方库`PyQt`或`Tkinter`来创建一个GUI桌面小程序,以便用户更方便地使用分Excel文件的功能。其中,`PyQt`的功能比`Tkinter`更强大,但学习曲线也更陡峭。以下是一个使用`Tkinter`库的示例代码: ```python import tkinter as tk from tkinter import filedialog from openpyxl import load_workbook from openpyxl import Workbook class ExcelSplitter: def __init__(self, master): self.master = master master.title("Excel工具") self.file_label = tk.Label(master, text="请选择要分的Excel文件:") self.file_label.pack() self.choose_file_button = tk.Button(master, text="选择文件", command=self.choose_file) self.choose_file_button.pack() self.row_option = tk.Radiobutton(master, text="按行数分", variable=self.split_method, value="row") self.row_option.pack() self.row_entry = tk.Entry(master) self.row_entry.pack() self.col_option = tk.Radiobutton(master, text="按列数分", variable=self.split_method, value="col") self.col_option.pack() self.col_entry = tk.Entry(master) self.col_entry.pack() self.split_button = tk.Button(master, text="分文件", command=self.split_file) self.split_button.pack() self.quit_button = tk.Button(master, text="退出", command=master.quit) self.quit_button.pack() self.split_method = tk.StringVar() self.split_method.set("row") def choose_file(self): self.file_path = filedialog.askopenfilename() def split_file(self): if not hasattr(self, "file_path"): return wb = load_workbook(filename=self.file_path) ws = wb.active data = [] for row in ws.iter_rows(values_only=True): data.append(row) if self.split_method.get() == "row": row_num = int(self.row_entry.get()) num_files = (len(data) + row_num - 1) // row_num for i in range(num_files): start_row = i * row_num end_row = min(start_row + row_num, len(data)) file_name = f"{i + 1}.xlsx" wb = Workbook() ws = wb.active for row in data[start_row:end_row]: ws.append(row) wb.save(file_name) elif self.split_method.get() == "col": col_num = int(self.col_entry.get()) num_files = (len(data[0]) + col_num - 1) // col_num for i in range(num_files): start_col = i * col_num end_col = min(start_col + col_num, len(data[0])) file_name = f"{i + 1}.xlsx" wb = Workbook() ws = wb.active for row in data: ws.append(row[start_col:end_col]) wb.save(file_name) root = tk.Tk() my_gui = ExcelSplitter(root) root.mainloop() ``` 希望这些代码对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值