excel拆分和合并代码的思路整合和工具打包

文章目录

拆分代码

处理核心流程

  1. 验证输入
  2. 读取数据
  3. 拆分数据
  4. 保存文件

拆分代码的核心逻辑可以总结为:"读取数据→计算拆分规则→按规则切割数据→保存小文件",具体步骤如下,结合代码细节一步步解释:

一、前提:用户输入准备

在拆分前,程序需要两个关键信息:

  1. 要拆分的Excel文件路径(用户通过"浏览"按钮选择)
  2. 每个小文件包含的行数(用户在输入框填写,默认10000行)

这两个信息会通过start_split函数做有效性检查

  • 检查文件是否存在(如果没选文件或文件路径无效,弹出警告)
  • 检查行数是否为正数(如果填0或负数或文字,弹出警告)

二、核心拆分函数split_excel的逻辑(重点)

当输入有效后,程序调用split_excel(file_path, rows_per_file)函数,这个函数是拆分的核心,步骤如下:

步骤1:读取Excel数据

pandas库的pd.read_excel(file_path)读取整个Excel文件,得到一个"数据框"df(可以理解为内存中的表格,包含所有行和列)。

然后用len(df)获取总数据行数(比如总共有25000行)。

步骤2:检查数据是否为空

如果总行数为0(即Excel里没数据),直接弹出"错误"提示,终止拆分。

步骤3:计算需要拆分成多少个文件

用"总行数 ÷ 每个文件的行数",并向上取整(避免最后一个文件行数不足时被漏掉)。

例如:总共有25000行,每个文件10000行 → 25000 ÷ 10000 = 2.5 → 向上取整后是3个文件。

代码里用math.ceil(total_rows / rows_per_file)实现这个计算。

步骤4:确定拆分后文件的保存位置和名称
  • 保存位置:和原文件在同一个文件夹(用os.path.dirname(file_path)获取原文件目录)。
  • 文件名称:原文件名 + "_拆分_序号"(比如原文件叫data.xlsx,拆分后是data_拆分_1.xlsxdata_拆分_2.xlsx...)。
步骤5:循环拆分并保存每个小文件

for循环遍历每个要生成的小文件(从0到"文件数量-1"),每次循环做3件事:

  1. 计算当前小文件的"起始行"和"结束行":

    • 起始行 = 序号 × 每个文件的行数(比如第1个文件是0×10000=0,第2个是1×10000=10000)
    • 结束行 = (序号+1)× 每个文件的行数,但如果超过总行数,就取总行数(避免越界)。
      例如总25000行,第3个文件的结束行是min(3×10000, 25000) = 25000
  2. 提取当前小文件的数据:

    df.iloc[start_row:end_row]从总数据中"切出"当前范围的行(比如第3个文件取20000到25000行),得到子数据框df_subset

  3. 保存子文件:

    df_subset.to_excel(output_path, index=False)将子数据框保存为Excel文件(index=False表示不保存行号,避免多余数据)。

步骤6:反馈结果
  • 如果所有文件都保存成功,弹出"成功"提示,显示拆分的文件数量(比如"共拆分成3个文件")。
  • 如果过程中出错(比如文件损坏、没有权限保存等),弹出"错误"提示,显示具体错误原因。

总结:拆分逻辑一句话概括

"先读全表,算好要拆成几个文件,再按行数一段一段切下来,每段存成一个新Excel,最后告诉用户结果"

整个过程不需要手动计算,程序会自动处理边界情况(比如最后一个文件行数不足),并通过图形界面让操作更简单。

python 复制代码
import pandas as pd
import os
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import math


def split_excel(file_path, rows_per_file):
    """将Excel文件按指定行数拆分成多个文件"""
    try:
        # 读取Excel文件
        df = pd.read_excel(file_path)
        total_rows = len(df)
        
        if total_rows == 0:
            messagebox.showerror("错误", "Excel文件中没有数据!")
            return
        
        # 计算需要拆分的文件数量
        num_files = math.ceil(total_rows / rows_per_file)
        
        # 获取文件目录和文件名(不含扩展名)
        file_dir = os.path.dirname(file_path)
        file_name = os.path.splitext(os.path.basename(file_path))[0]
        
        # 拆分文件
        for i in range(num_files):
            start_row = i * rows_per_file
            end_row = min((i + 1) * rows_per_file, total_rows)
            
            # 提取数据子集
            df_subset = df.iloc[start_row:end_row]
            
            # 生成输出文件名
            output_filename = f"{file_name}_拆分_{i+1}.xlsx"
            output_path = os.path.join(file_dir, output_filename)
            
            # 保存拆分后的文件
            df_subset.to_excel(output_path, index=False)
        
        messagebox.showinfo("成功", f"文件拆分完成!\n共拆分成 {num_files} 个文件\n每个文件约 {rows_per_file} 行")
        
    except Exception as e:
        messagebox.showerror("错误", f"拆分过程中出现错误:{str(e)}")


def select_file():
    """选择Excel文件"""
    file_path = filedialog.askopenfilename(
        title="选择要拆分的Excel文件",
        filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
    )
    if file_path:
        file_var.set(file_path)
        
        # 自动读取文件信息
        try:
            df = pd.read_excel(file_path)
            total_rows = len(df)
            info_var.set(f"文件总行数: {total_rows} 行")
        except Exception as e:
            info_var.set("无法读取文件信息")


def start_split():
    """开始拆分"""
    file_path = file_var.get()
    
    if not file_path or not os.path.exists(file_path):
        messagebox.showwarning("警告", "请先选择有效的Excel文件!")
        return
    
    try:
        rows_per_file = int(rows_var.get())
        if rows_per_file <= 0:
            messagebox.showwarning("警告", "请输入有效的行数(大于0)!")
            return
    except ValueError:
        messagebox.showwarning("警告", "请输入有效的数字!")
        return
    
    split_excel(file_path, rows_per_file)


# 创建主窗口
root = tk.Tk()
root.title("Excel文件拆分工具:数据分析")
root.geometry("550x200")

# 变量
file_var = tk.StringVar()
rows_var = tk.StringVar(value="10000")  # 默认10000行
info_var = tk.StringVar(value="请选择Excel文件")

# 界面布局
frame = ttk.Frame(root, padding="15")
frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

# 文件选择行
ttk.Label(frame, text="选择Excel文件:").grid(row=0, column=0, sticky=tk.W, pady=5)
ttk.Entry(frame, textvariable=file_var, width=40).grid(row=0, column=1, padx=5, pady=5)
ttk.Button(frame, text="浏览", command=select_file).grid(row=0, column=2, padx=5, pady=5)

# 文件信息显示
ttk.Label(frame, textvariable=info_var, foreground="blue").grid(row=1, column=1, sticky=tk.W, pady=2)

# 行数设置行
ttk.Label(frame, text="每份文件行数:").grid(row=2, column=0, sticky=tk.W, pady=10)
ttk.Entry(frame, textvariable=rows_var, width=15).grid(row=2, column=1, sticky=tk.W, padx=5, pady=10)
ttk.Label(frame, text="行").grid(row=2, column=1, sticky=tk.W, padx=100, pady=10)

# 开始拆分按钮
ttk.Button(frame, text="开始拆分", command=start_split).grid(row=3, column=1, pady=20)

# 配置网格权重
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)

root.mainloop()

合并代码

合并代码的核心逻辑可以总结为:"选一个基准文件→自动找相似文件→确认后合并→统一格式并保存",整个过程不需要手动逐个选择文件,重点在"智能识别相似文件"和"兼容不同格式的表格"。下面分步骤拆解逻辑:

一、前提:用户输入准备

程序需要用户先做一件事:选择一个"基准Excel文件"(通过"浏览"按钮选择)。这个文件的作用是"模板"------程序会以它的文件名为线索,去同目录下找其他"长得像"的文件。

二、核心合并函数combine_similar_files的逻辑(重点)

当用户点击"开始智能合并"后,程序调用这个函数,核心步骤如下:

步骤1:定位文件目录并收集所有Excel文件
  • 先确定基准文件所在的文件夹(比如基准文件在D:\数据,就只在这个文件夹里找其他文件)。
  • 遍历这个文件夹,把所有以.xlsx.xls结尾的文件都挑出来(这些是潜在的合并对象)。
  • 如果文件夹里没有Excel文件,直接弹"错误"提示(比如"目录中没有找到Excel文件")。
步骤2:从基准文件名中提取"相似模式"(核心!)

这是程序"智能"的关键------通过基准文件名,提炼出一个"共同特征",用来判断其他文件是否相似。比如:

  • 若基准文件是销售数据_1.xlsx,会提炼出销售数据(去掉数字和后缀);
  • 若基准文件是报表-202401.xlsx,会提炼出报表-2024(去掉末尾的日期序号);
  • 若基准文件是客户列表 v2.xlsx,会提炼出客户列表(去掉版本号)。

具体由extract_common_pattern函数实现,逻辑是:

  1. 先去掉文件后缀(如.xlsx),只看文件名主体;
  2. 去掉文件名里的数字(比如文件123文件);
  3. 按常见分隔符(_-、空格)拆分,取前面的部分(比如数据_拆分_3数据_拆分);
  4. 如果以上都不适用,就用整个文件名主体作为模式。
步骤3:用"相似模式"筛选同目录的文件

有了"相似模式"后,程序会在步骤1收集的所有Excel文件中,筛选出符合模式的文件。由find_similar_files函数实现,比如:

  • 模式是销售数据,则销售数据_2.xlsx销售数据_3.xlsx会被选中(包含销售数据);
  • 模式是报表-2024,则报表-202402.xlsx报表-202403.xlsx会被选中(以模式开头)。

筛选时会自动排除基准文件本身吗?不,会包含基准文件(最终合并的文件列表里,基准文件是第一个)。

步骤4:让用户确认要合并的文件

如果筛选出的相似文件只有1个(也就是只有基准文件自己),会弹"提示"说"没有找到其他相似文件",终止流程。

如果有多个,会列出所有文件(比如"销售数据_1.xlsx、销售数据_2.xlsx、销售数据_3.xlsx"),让用户选"是"或"否"------用户确认后才继续合并。

步骤5:合并文件数据(处理格式兼容)

这一步是实际合并数据,核心是解决"不同文件列不一致"的问题(比如A文件有"姓名、金额",B文件只有"姓名")。步骤:

  1. 逐个读取相似文件的表格数据(用pd.read_excel),得到每个文件的"数据框"(内存中的表格)。
  2. 第一个文件的数据作为"基准结构",后续文件的数据会和它对齐:
    • 如果列名完全一致(比如都有"姓名、金额"),直接拼接(按行叠加);
    • 如果列名不一致(比如B文件缺"金额"),则给B文件自动补一个"金额"列,空值填充(保证合并后所有列名和第一个文件一致)。
  3. pd.concat把所有数据框"拼"成一个大的数据框(df_combined)。
步骤6:保存合并结果(避免文件名冲突)
  • 生成结果文件名:用步骤2提炼的"相似模式"+"_合并结果.xlsx"(比如销售数据_合并结果.xlsx)。
  • 检查是否有重名:如果同名文件已存在,自动加序号(比如销售数据_合并结果(1).xlsx(2).xlsx)。
  • 保存到基准文件所在的文件夹(用to_excel函数)。
步骤7:反馈合并结果
  • 成功:弹窗显示"合并了3个文件,总行数1500,保存路径XXX";
  • 失败:弹窗显示具体错误(比如"某文件损坏无法读取""没有权限保存")。

总结:合并逻辑一句话概括

"以一个文件为模板,自动找到同目录下名字相似的其他Excel文件,对齐它们的表格列,拼在一起后存成一个新文件,过程中让用户确认并反馈结果"

这个逻辑的核心优势是"智能找文件"(不用手动选)和"自动兼容格式"(不怕列不一致),特别适合处理按序号拆分的文件(如数据1数据2)或按时间拆分的文件(如202401报表202402报表)。

python 复制代码
import pandas as pd
import os
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
from collections import defaultdict


def combine_similar_files(selected_file):
    """合并与选定文件类似的所有Excel文件"""
    try:
        file_dir = os.path.dirname(selected_file)
        file_name = os.path.basename(selected_file)
        
        # 获取目录下所有Excel文件
        listdir = os.listdir(file_dir)
        excel_files = [f for f in listdir if f.endswith(('.xlsx', '.xls'))]
        
        if not excel_files:
            messagebox.showerror("错误", "目录中没有找到 Excel 文件!")
            return
        
        # 分析选定文件的特征(文件名模式)
        base_name = file_name
        # 尝试提取共同前缀(去除序号、版本号等)
        common_patterns = extract_common_pattern(base_name)
        
        # 根据特征筛选相似文件
        similar_files = find_similar_files(excel_files, common_patterns, base_name)
        
        if len(similar_files) <= 1:
            messagebox.showinfo("提示", f"没有找到与 '{file_name}' 相似的其他文件")
            return
        
        # 显示找到的相似文件列表
        file_list = "\n".join(similar_files)
        confirm = messagebox.askyesno(
            "确认合并", 
            f"找到以下相似文件,是否合并?\n\n{file_list}\n\n共 {len(similar_files)} 个文件"
        )
        
        if not confirm:
            return
        
        # 合并文件
        df_combined = None
        for filename in similar_files:
            file_path = os.path.join(file_dir, filename)
            df_temp = pd.read_excel(file_path)
            
            if df_combined is None:
                df_combined = df_temp
            else:
                # 检查列结构是否一致
                if set(df_combined.columns) != set(df_temp.columns):
                    # 如果不一致,尝试对齐列
                    df_temp = df_temp.reindex(columns=df_combined.columns, fill_value=None)
                
                df_combined = pd.concat([df_combined, df_temp], ignore_index=True, sort=False)
        
        # 生成输出文件名
        output_name = generate_output_name(common_patterns, base_name)
        output_path = os.path.join(file_dir, f"{output_name}_合并结果.xlsx")
        
        # 避免文件名冲突
        counter = 1
        while os.path.exists(output_path):
            output_path = os.path.join(file_dir, f"{output_name}_合并结果({counter}).xlsx")
            counter += 1
        
        # 保存合并后的文件
        df_combined.to_excel(output_path, index=False)
        
        messagebox.showinfo(
            "成功", 
            f"数据合并完成!\n"
            f"合并了 {len(similar_files)} 个文件\n"
            f"保存位置:{output_path}\n"
            f"总行数:{len(df_combined)}"
        )
        
    except Exception as e:
        messagebox.showerror("错误", f"合并过程中出现错误:{str(e)}")


def extract_common_pattern(filename):
    """提取文件名的共同模式"""
    # 去除扩展名
    name_without_ext = os.path.splitext(filename)[0]
    
    patterns = []
    
    # 常见模式识别
    # 1. 按数字分割(如:文件1.xlsx, 文件2.xlsx)
    if any(char.isdigit() for char in name_without_ext):
        # 提取非数字部分作为模式
        non_digit_parts = []
        current_part = ""
        for char in name_without_ext:
            if char.isdigit():
                if current_part:
                    non_digit_parts.append(current_part)
                    current_part = ""
            else:
                current_part += char
        if current_part:
            non_digit_parts.append(current_part)
        
        if non_digit_parts:
            patterns.append(''.join(non_digit_parts).strip(' _-'))
    
    # 2. 按常见分隔符分割
    separators = ['_', '-', ' ']
    for sep in separators:
        if sep in name_without_ext:
            parts = name_without_ext.split(sep)
            # 取前面几个部分作为模式
            if len(parts) > 1:
                patterns.append(sep.join(parts[:-1]))
    
    # 3. 如果以上都不适用,使用整个文件名(不含扩展名)
    if not patterns:
        patterns.append(name_without_ext)
    
    return patterns


def find_similar_files(all_files, patterns, base_file):
    """根据模式查找相似文件"""
    similar_files = [base_file]  # 包含选定的文件
    
    for pattern in patterns:
        if pattern:  # 确保模式不为空
            for filename in all_files:
                if filename == base_file:
                    continue  # 跳过选定的文件本身
                
                name_without_ext = os.path.splitext(filename)[0]
                
                # 多种匹配策略
                if (pattern in filename or 
                    pattern in name_without_ext or
                    filename.startswith(pattern) or
                    name_without_ext.startswith(pattern)):
                    similar_files.append(filename)
    
    # 去重并返回
    return list(dict.fromkeys(similar_files))


def generate_output_name(patterns, base_file):
    """生成输出文件名"""
    if patterns:
        # 使用最长的模式作为基础名称
        longest_pattern = max(patterns, key=len)
        if len(longest_pattern) > 3:  # 确保模式有足够长度
            return longest_pattern
    
    # 回退方案:使用基础文件名(不含扩展名)
    return os.path.splitext(base_file)[0]


def select_file():
    """选择Excel文件"""
    file_path = filedialog.askopenfilename(
        title="选择要合并的Excel文件",
        filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
    )
    if file_path:
        file_var.set(file_path)
        # 显示选中的文件名
        file_name = os.path.basename(file_path)
        info_var.set(f"已选择: {file_name}")


def start_combine():
    """开始合并"""
    file_path = file_var.get()
    
    if not file_path or not os.path.exists(file_path):
        messagebox.showwarning("警告", "请先选择有效的Excel文件!")
        return
    
    combine_similar_files(file_path)


# 创建主窗口
root = tk.Tk()
root.title("智能文件合并:数据分析")
root.geometry("600x180")

# 变量
file_var = tk.StringVar()
info_var = tk.StringVar(value="请选择Excel文件")

# 界面布局
frame = ttk.Frame(root, padding="15")
frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

# 文件选择行
ttk.Label(frame, text="选择基准Excel文件:").grid(row=0, column=0, sticky=tk.W, pady=5)
ttk.Entry(frame, textvariable=file_var, width=50).grid(row=0, column=1, padx=5, pady=5)
ttk.Button(frame, text="浏览", command=select_file).grid(row=0, column=2, padx=5, pady=5)

# 文件信息显示
ttk.Label(frame, textvariable=info_var, foreground="blue").grid(row=1, column=1, sticky=tk.W, pady=5)

# 说明文字
info_text = "系统会自动查找与选定文件相似的其他文件进行合并\n(基于文件名模式识别,如:文件1.xlsx, 文件2.xlsx)"
ttk.Label(frame, text=info_text, foreground="gray", font=("Arial", 9)).grid(row=2, column=1, sticky=tk.W, pady=5)

# 开始合并按钮
ttk.Button(frame, text="开始智能合并", command=start_combine).grid(row=3, column=1, pady=15)

# 配置网格权重
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)

root.mainloop()

整合代码

python 复制代码
import pandas as pd
import os
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import math
import logging
import sys

# --- 1. 日志配置 ---
# 配置日志输出到控制台 (CMD) 和文件,用于调试和运行状态跟踪
def setup_logging():
    # 使用 StreamHandler 将日志输出到标准输出,确保在 CMD 中可见
    logging.basicConfig(
        level=logging.INFO, # 默认级别为 INFO,会输出 INFO, WARNING, ERROR, CRITICAL
        format='%(asctime)s - %(levelname)s - %(message)s',
        handlers=[
            logging.StreamHandler(sys.stdout)
        ]
    )
    logging.info("日志初始化完成。Excel 处理工具启动。")

# --- 2. 文件合并逻辑 (Merge Functions) ---

def extract_common_pattern(filename):
    """提取文件名的共同模式(用于智能识别相似文件)"""
    name_without_ext = os.path.splitext(filename)[0]
    logging.debug(f"分析文件名模式: {name_without_ext}")
    
    patterns = []
    
    # 1. 按数字分割(提取非数字部分作为模式)
    non_digit_parts = []
    current_part = ""
    for char in name_without_ext:
        if char.isdigit():
            if current_part:
                non_digit_parts.append(current_part)
                current_part = ""
        else:
            current_part += char
    if current_part:
        non_digit_parts.append(current_part)
    
    pattern1 = ''.join(non_digit_parts).strip(' _-')
    if pattern1:
        patterns.append(pattern1)
        logging.debug(f"模式 1 (非数字部分): {pattern1}")

    # 2. 按常见分隔符分割(提取前缀)
    separators = ['_', '-']
    for sep in separators:
        if sep in name_without_ext:
            parts = name_without_ext.split(sep)
            if len(parts) > 1:
                pattern2 = sep.join(parts[:-1]).strip()
                if pattern2:
                    patterns.append(pattern2)
                    logging.debug(f"模式 2 (分隔符 '{sep}' 前缀): {pattern2}")
    
    # 3. 回退方案:使用整个文件名(不含扩展名)
    if not patterns:
        patterns.append(name_without_ext)
    
    return list(set(patterns))


def find_similar_files(all_files, patterns, base_file):
    """根据提取的模式查找相似文件"""
    similar_files = [base_file] 
    logging.info(f"基准文件: {base_file}")
    
    for pattern in patterns:
        logging.debug(f"检查模式: '{pattern}'")
        if not pattern:
            continue
        
        for filename in all_files:
            if filename == base_file:
                continue 
            
            name_without_ext = os.path.splitext(filename)[0]
            
            # 匹配逻辑:文件名中包含模式,或文件名以模式开头
            if (pattern in name_without_ext or
                name_without_ext.startswith(pattern)):
                
                if filename not in similar_files:
                    similar_files.append(filename)
                    logging.info(f"找到相似文件: {filename}")
    
    return list(dict.fromkeys(similar_files))


def generate_output_name(patterns, base_file):
    """生成输出文件名"""
    # 优先使用最长的、有意义的模式
    meaningful_patterns = [p for p in patterns if len(p) > 3]
    if meaningful_patterns:
        longest_pattern = max(meaningful_patterns, key=len)
        return longest_pattern
    
    # 回退到基准文件名(不含扩展名)
    return os.path.splitext(base_file)[0]


def combine_similar_files(selected_file):
    """执行文件合并操作的核心逻辑"""
    logging.info("-" * 50)
    logging.info(f"开始合并操作,基准文件: {selected_file}")
    
    try:
        file_dir = os.path.dirname(selected_file)
        file_name = os.path.basename(selected_file)
        
        # 1. 查找所有 Excel 文件
        listdir = os.listdir(file_dir)
        excel_files = [f for f in listdir if f.lower().endswith(('.xlsx', '.xls', '.xlsm'))]
        
        if not excel_files:
            logging.error("目录中未找到 Excel 文件。")
            messagebox.showerror("错误", "目录中没有找到 Excel 文件!")
            return
        
        # 2. 分析模式并查找相似文件
        common_patterns = extract_common_pattern(file_name)
        similar_files = find_similar_files(excel_files, common_patterns, file_name)
        
        if len(similar_files) <= 1:
            logging.warning("未找到相似的其他文件。")
            messagebox.showinfo("提示", f"没有找到与 '{file_name}' 相似的其他文件")
            return
        
        # 3. 确认合并
        file_list = "\n".join(similar_files)
        logging.info(f"共找到 {len(similar_files)} 个文件准备合并。")
        
        confirm = messagebox.askyesno(
            "确认合并", 
            f"找到以下相似文件,是否合并?\n\n{file_list}\n\n共 {len(similar_files)} 个文件"
        )
        
        if not confirm:
            logging.info("用户取消了合并操作。")
            return
        
        # 4. 执行合并
        df_combined = pd.DataFrame() 
        
        for filename in similar_files:
            file_path = os.path.join(file_dir, filename)
            logging.info(f"正在读取文件: {filename}")
            
            try:
                # 使用 openpyxl 引擎处理 .xlsx 文件
                df_temp = pd.read_excel(file_path, engine='openpyxl')
                df_combined = pd.concat([df_combined, df_temp], ignore_index=True, sort=False)
                logging.debug(f"文件 {filename} 读取成功,已合并。")
                
            except Exception as read_e:
                logging.error(f"读取文件 {filename} 时出错: {str(read_e)}")
                messagebox.showwarning("文件读取警告", f"无法读取文件 '{filename}'。该文件将被跳过。")
                continue

        if df_combined.empty:
            messagebox.showerror("错误", "所有文件读取失败,无法合并。")
            logging.error("合并后的 DataFrame 为空。")
            return

        # 5. 保存结果
        output_name = generate_output_name(common_patterns, file_name)
        output_path = os.path.join(file_dir, f"{output_name}_合并结果.xlsx")
        
        # 避免文件名冲突
        counter = 1
        while os.path.exists(output_path):
            output_path = os.path.join(file_dir, f"{output_name}_合并结果({counter}).xlsx")
            counter += 1
            
        logging.info(f"保存合并结果到: {output_path}")
        
        df_combined.to_excel(output_path, index=False, engine='openpyxl')
        
        messagebox.showinfo(
            "成功", 
            f"数据合并完成!\n合并了 {len(similar_files)} 个文件\n保存位置:{output_path}\n总行数:{len(df_combined)}"
        )
        logging.info(f"合并成功,总行数: {len(df_combined)}")
        
    except Exception as e:
        logging.critical(f"合并过程中发生致命错误: {str(e)}")
        messagebox.showerror("错误", f"合并过程中出现错误:{str(e)}")


# --- 3. 文件拆分逻辑 (Split Functions) ---

def split_excel(file_path, rows_per_file):
    """执行文件拆分操作的核心逻辑"""
    logging.info("-" * 50)
    logging.info(f"开始拆分操作,文件: {file_path}")
    logging.info(f"每份文件行数: {rows_per_file}")

    try:
        df = pd.read_excel(file_path, engine='openpyxl')
        total_rows = len(df)
        
        if total_rows == 0:
            messagebox.showerror("错误", "Excel文件中没有数据!")
            logging.error("待拆分文件为空。")
            return
        
        # 计算文件数量
        num_files = math.ceil(total_rows / rows_per_file)
        
        file_dir = os.path.dirname(file_path)
        file_name = os.path.splitext(os.path.basename(file_path))[0]
        
        logging.info(f"总行数: {total_rows} 行,将拆分成 {num_files} 个文件。")
        
        # 拆分和保存
        for i in range(num_files):
            start_row = i * rows_per_file
            end_row = min((i + 1) * rows_per_file, total_rows)
            
            df_subset = df.iloc[start_row:end_row]
            
            output_filename = f"{file_name}_拆分_{i+1}.xlsx"
            output_path = os.path.join(file_dir, output_filename)
            
            logging.info(f"保存第 {i+1}/{num_files} 份文件 (行 {start_row+1} 到 {end_row}) 到: {output_path}")

            df_subset.to_excel(output_path, index=False, engine='openpyxl')
        
        logging.info(f"文件拆分操作成功完成。")
        messagebox.showinfo("成功", f"文件拆分完成!\n共拆分成 {num_files} 个文件\n每个文件约 {rows_per_file} 行")
        
    except Exception as e:
        logging.critical(f"拆分过程中发生致命错误: {str(e)}")
        messagebox.showerror("错误", f"拆分过程中出现错误:{str(e)}")


# --- 4. Tkinter GUI 界面 ---

class ExcelProcessorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Excel文件智能处理工具 (合并与拆分)")
        # 优化界面样式
        try:
            style = ttk.Style()
            style.theme_use('vista') # 尝试使用Windows风格主题
            style.configure('TButton', font=('Arial', 10, 'bold'), padding=6)
        except:
             pass # 如果主题不存在,保持默认
             
        self.root.geometry("650x300")
        self.root.resizable(False, False)

        # 变量初始化
        self.merge_file_var = tk.StringVar()
        self.merge_info_var = tk.StringVar(value="请选择Excel文件作为合并基准")
        self.split_file_var = tk.StringVar()
        self.split_rows_var = tk.StringVar(value="10000") 
        self.split_info_var = tk.StringVar(value="请选择待拆分的Excel文件")

        # 初始化日志
        setup_logging()
        
        # 创建 Notebook (标签页界面)
        self.notebook = ttk.Notebook(root)
        self.notebook.pack(pady=10, padx=10, expand=True, fill="both")

        # 创建标签页框架
        self.merge_frame = ttk.Frame(self.notebook, padding="15 15 15 15")
        self.split_frame = ttk.Frame(self.notebook, padding="15 15 15 15")
        
        self.notebook.add(self.merge_frame, text="文件智能合并")
        self.notebook.add(self.split_frame, text="文件按行拆分")

        # 构建标签页 UI
        self._build_merge_tab()
        self._build_split_tab()

    # --- UI 构建器 ---

    def _build_merge_tab(self):
        self.merge_frame.columnconfigure(1, weight=1)
        
        # 1. 文件选择
        ttk.Label(self.merge_frame, text="选择基准Excel文件:", font=('Arial', 10, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=10)
        ttk.Entry(self.merge_frame, textvariable=self.merge_file_var, width=50).grid(row=0, column=1, padx=5, pady=10, sticky=(tk.W, tk.E))
        ttk.Button(self.merge_frame, text="浏览", command=self._select_merge_file).grid(row=0, column=2, padx=5, pady=10)

        # 2. 文件信息
        ttk.Label(self.merge_frame, textvariable=self.merge_info_var, foreground="#0056b3", font=("Arial", 10)).grid(row=1, column=1, sticky=tk.W, pady=5)

        # 3. 功能说明
        info_text = "功能说明:系统将根据文件名模式(例如:文件_1, 文件_2)自动查找同目录下所有相似文件,并将它们合并。\n请确保所有文件的表头结构大致一致。"
        ttk.Label(self.merge_frame, text=info_text, foreground="gray", font=("Arial", 9)).grid(row=2, column=0, columnspan=3, sticky=tk.W, pady=10)

        # 4. 开始按钮
        ttk.Button(self.merge_frame, text="⭐ 开始智能合并 ⭐", command=self._start_merge, style='TButton').grid(row=3, column=1, pady=20)
    
    
    def _build_split_tab(self):
        self.split_frame.columnconfigure(1, weight=1)

        # 1. 文件选择
        ttk.Label(self.split_frame, text="选择待拆分的Excel文件:", font=('Arial', 10, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=10)
        ttk.Entry(self.split_frame, textvariable=self.split_file_var, width=50).grid(row=0, column=1, padx=5, pady=10, sticky=(tk.W, tk.E))
        ttk.Button(self.split_frame, text="浏览", command=self._select_split_file).grid(row=0, column=2, padx=5, pady=10)

        # 2. 文件信息
        ttk.Label(self.split_frame, textvariable=self.split_info_var, foreground="#0056b3", font=("Arial", 10)).grid(row=1, column=1, sticky=tk.W, pady=5)

        # 3. 行数设置
        ttk.Label(self.split_frame, text="每份文件行数:").grid(row=2, column=0, sticky=tk.W, pady=10)
        ttk.Entry(self.split_frame, textvariable=self.split_rows_var, width=15).grid(row=2, column=1, sticky=tk.W, padx=5, pady=10)
        ttk.Label(self.split_frame, text="行 (默认 10000)").grid(row=2, column=1, sticky=tk.W, padx=120, pady=10)

        # 4. 开始按钮
        ttk.Button(self.split_frame, text="⚙️ 开始拆分 ⚙️", command=self._start_split, style='TButton').grid(row=3, column=1, pady=20)

    # --- 5. 事件处理器 ---

    def _select_file(self, file_var, info_var, mode="select"):
        """通用文件选择逻辑,并自动读取文件信息(总行数)"""
        title = "选择 Excel 文件"
             
        file_path = filedialog.askopenfilename(
            title=title,
            filetypes=[("Excel files", "*.xlsx *.xls *.xlsm"), ("All files", "*.*")]
        )
        if file_path:
            file_var.set(file_path)
            file_name = os.path.basename(file_path)
            
            try:
                # 尝试读取文件行数
                df = pd.read_excel(file_path, engine='openpyxl')
                total_rows = len(df)
                
                if mode == "split":
                    info_var.set(f"已选择: {file_name} | 文件总行数: {total_rows} 行")
                    logging.info(f"拆分文件已选择: {file_path}. 总行数: {total_rows}")
                else:
                    info_var.set(f"已选择: {file_name} | 总行数: {total_rows} 行")
                    logging.info(f"合并基准文件已选择: {file_path}. 总行数: {total_rows}")
            except Exception as e:
                info_var.set(f"已选择: {file_name} | 无法读取文件信息或文件损坏。")
                logging.error(f"读取文件信息出错: {e}")

    def _select_merge_file(self):
        self._select_file(self.merge_file_var, self.merge_info_var, mode="merge")

    def _select_split_file(self):
        self._select_file(self.split_file_var, self.split_info_var, mode="split")


    def _start_merge(self):
        file_path = self.merge_file_var.get()
        if not file_path or not os.path.exists(file_path):
            messagebox.showwarning("警告", "请先选择有效的Excel文件!")
            return
        # 运行合并逻辑
        combine_similar_files(file_path)
        
    def _start_split(self):
        file_path = self.split_file_var.get()
        if not file_path or not os.path.exists(file_path):
            messagebox.showwarning("警告", "请先选择有效的Excel文件!")
            return
        
        try:
            rows_per_file = int(self.split_rows_var.get())
            if rows_per_file <= 0:
                messagebox.showwarning("警告", "请输入有效的行数(大于0)!")
                return
        except ValueError:
            messagebox.showwarning("警告", "每份文件行数必须是有效的数字!")
            return
        
        # 运行拆分逻辑
        split_excel(file_path, rows_per_file)


# --- 6. 主程序入口 ---
if __name__ == '__main__':
    root = tk.Tk()
    app = ExcelProcessorApp(root)
    root.mainloop()

执行打包命令

python 复制代码
pyinstaller --onefile --console --upx-dir D:\upx excel_tool.py
相关推荐
天才测试猿4 小时前
黑盒测试用例的四种设计方法
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
B站_计算机毕业设计之家4 小时前
机器学习:基于大数据的基金数据分析可视化系统 股票数据 金融数据 股价 Django框架 大数据技术(源码) ✅
大数据·python·金融·数据分析·股票·etf·基金
*才华有限公司*5 小时前
《爬虫进阶之路:从模拟浏览器到破解动态加载的实战指南》
开发语言·python
深蓝电商API5 小时前
爬虫+Redis:如何实现分布式去重与任务队列?
redis·分布式·爬虫·python
我是华为OD~HR~栗栗呀5 小时前
华为OD-23届考研-测试面经
java·c++·python·华为od·华为·面试·单元测试
gc_22995 小时前
学习Python中Selenium模块的基本用法(20:安装Selenium IDE)
python·selenium
程序员爱钓鱼5 小时前
Python编程实战 · 基础入门篇 | 数据类型简介:数字、字符串、布尔值
后端·python
Python图像识别6 小时前
73_基于深度学习的水面漂浮垃圾检测系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
python·深度学习·yolo
mit6.8246 小时前
[tile-lang] 语言接口 | `T.prim_func` & `@tilelang.jit` | 底层原理
python