查看: 121|回复: 3

Python批量删除Excel列前缀:openpyxl脚本实现与优化

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
批量删除Excel某一列单元格的固定前缀,是数据清洗中很常见的脚本需求。例如采购订单号“PO-2023-10001”只需保留“10001”,员工工号“DEV001”只需保留“001”,产品SKU“SHOES-BLACK-42-1001”只需保留最后的“1001”。手工查找替换在数据量达到数百行后效率很低,而且遇到前缀长度不一致时容易出错。本文围绕Python openpyxl实现一个可复用的列前缀删除脚本,说明函数参数、调用方式、扩展思路和常见问题排查。

一、技术选型与实现目标

原文对比了VBA宏、openpyxl、pandas、Java POI、C# NPOI等方案。VBA不需要额外环境,但代码维护和安全性较弱;pandas适合大数据分析,但内存占用高;POI和NPOI更适合企业系统集成。综合开发效率和部署成本,选择Python openpyxl。它可以直接读写xlsx/xlsm/xltx/xltm文件,跨平台,代码量较小,适合做办公自动化脚本。

工具需要完成这些动作:读取指定Excel文件;定位工作表和工作列;对每个字符串单元格识别并删除前缀;支持固定长度和分隔符两种模式;把修改结果保存为新文件。

二、环境准备

建议使用Python 3.7及以上版本,安装openpyxl:
  1. pip install openpyxl
复制代码

openpyxl负责解析Excel内部的XML结构,读写单元格、工作表、公式等内容。脚本运行时不需要安装Microsoft Excel。

三、核心函数实现

下面的函数按列索引处理指定工作表,支持prefix_length和delimiter两种参数:
  1. import openpyxl
  2. from openpyxl.utils import get_column_letter
  3. import os
  4. def remove_prefix_from_column(file_path, sheet_name, column_index, prefix_length=None, delimiter=None):
  5.     """
  6.     批量删除Excel指定列单元格内容的前缀
  7.     参数:
  8.     file_path: Excel文件路径
  9.     sheet_name: 工作表名称
  10.     column_index: 列索引(从1开始)
  11.     prefix_length: 要删除的前缀长度(可选)
  12.     delimiter: 分隔符,删除分隔符前的内容(可选)
  13.     """
  14.     # 加载工作簿
  15.     wb = openpyxl.load_workbook(file_path)
  16.     try:
  17.         # 获取工作表
  18.         sheet = wb[sheet_name]
  19.     except KeyError:
  20.         print(f"错误:工作表 '{sheet_name}' 不存在")
  21.         return False
  22.     # 获取列字母
  23.     column_letter = get_column_letter(column_index)
  24.     # 遍历列中的每个单元格
  25.     for cell in sheet[column_letter]:
  26.         if cell.value and isinstance(cell.value, str):
  27.             original_value = cell.value
  28.             # 根据参数选择处理方式
  29.             if prefix_length:
  30.                 # 按固定长度删除前缀
  31.                 if len(original_value) > prefix_length:
  32.                     cell.value = original_value[prefix_length:]
  33.             elif delimiter:
  34.                 # 按分隔符删除前缀
  35.                 if delimiter in original_value:
  36.                     cell.value = original_value.split(delimiter)[-1]
  37.     # 保存修改
  38.     new_file_path = f"{os.path.splitext(file_path)[0]}_modified.xlsx"
  39.     wb.save(new_file_path)
  40.     print(f"处理完成,新文件已保存为: {new_file_path}")
  41.     return True
  42. # 使用示例
  43. if __name__ == "__main__":
  44.     # 按固定长度删除前缀
  45.     remove_prefix_from_column("data.xlsx", "Sheet1", 2, prefix_length=4)
  46.     # 按分隔符删除前缀
  47.     # remove_prefix_from_column("data.xlsx", "Sheet1", 2, delimiter="-")
复制代码

函数参数含义如下:file_path是输入文件路径;sheet_name是工作表名称;column_index从1开始计数;prefix_length表示删除前N个字符;delimiter表示删除分隔符之前的内容。保存时不会覆盖原文件,而是在原文件名后追加“_modified.xlsx”,这一点在批量处理前要特别注意,避免找不到输出文件。

四、代码逻辑解析

load_workbook()负责加载工作簿;wb[sheet_name]按名称获取工作表,如果名称不存在会抛出KeyError,代码捕获后返回False;get_column_letter()把数字列索引转换为Excel列字母,例如1转换为A、2转换为B;sheet[column_letter]取得整列单元格。

处理时只对非空且类型为字符串的单元格生效。固定长度模式下,只有原值长度大于prefix_length才截取,避免短字符串被清空;分隔符模式下,用split(delimiter)[-1]保留最后一段。由于代码先判断prefix_length,再使用elif判断delimiter,两个参数同时传入时固定长度模式优先。prefix_length为0或delimiter为空字符串时不会进入对应分支,实际调用时应传入有效值。

另一个容易忽略的点是:sheet[column_letter]会遍历整列,如果第一行是表头,表头也会被处理。实际使用时可以先确认表头内容,或者根据业务在遍历时跳过首行。

五、多条件组合处理

如果业务既要支持固定长度,又要支持指定前缀字符串,可以把单元格处理逻辑抽成独立函数:
  1. def advanced_remove_prefix(cell_value, prefix_length=None, delimiter=None, prefix_str=None):
  2.     """
  3.     高级前缀处理函数
  4.     """
  5.     if not cell_value or not isinstance(cell_value, str):
  6.         return cell_value
  7.     if prefix_length:
  8.         if len(cell_value) > prefix_length:
  9.             return cell_value[prefix_length:]
  10.     if delimiter:
  11.         if delimiter in cell_value:
  12.             return cell_value.split(delimiter)[-1]
  13.     if prefix_str:
  14.         if cell_value.startswith(prefix_str):
  15.             return cell_value[len(prefix_str):]
  16.     return cell_value
复制代码

这个函数把“按长度”“按分隔符”“按明确前缀字符串”三种规则集中起来。prefix_str适合前缀内容固定但长度不固定的场景,例如统一删除“PRD-”这类标识。

六、批量处理多个文件

文件夹内存在多个Excel文件时,可以遍历目录并复用核心函数:
  1. def batch_process_files(folder_path, sheet_name, column_index, **kwargs):
  2.     """
  3.     批量处理文件夹中的所有Excel文件
  4.     """
  5.     for filename in os.listdir(folder_path):
  6.         if filename.endswith(".xlsx") or filename.endswith(".xls"):
  7.             file_path = os.path.join(folder_path, filename)
  8.             print(f"正在处理文件: {filename}")
  9.             remove_prefix_from_column(file_path, sheet_name, column_index, **kwargs)
复制代码

这里通过**kwargs把prefix_length或delimiter原样传给核心函数。需要注意,openpyxl主要面向xlsx等格式,旧版xls文件并不在它的标准支持范围内,批量筛选时应对扩展名和实际文件格式做校验。

七、图形界面封装

给非技术用户使用时,可以用PySimpleGUI封装一个简单界面,把文件路径、工作表名、列索引和处理模式暴露出来:
  1. import PySimpleGUI as sg
  2. def create_gui():
  3.     layout = [
  4.         [sg.Text("Excel文件路径:"), sg.Input(), sg.FileBrowse()],
  5.         [sg.Text("工作表名称:"), sg.InputText("Sheet1")],
  6.         [sg.Text("列索引:"), sg.InputText("1")],
  7.         [sg.Radio("按长度删除", "prefix_type", default=True, key="prefix_length"),
  8.          sg.InputText("4", size=(5,1))],
  9.         [sg.Radio("按分隔符删除", "prefix_type", key="delimiter"),
  10.          sg.InputText("-", size=(5,1))],
  11.         [sg.Button("执行"), sg.Button("退出")]
  12.     ]
  13.     window = sg.Window("Excel前缀批处理工具", layout)
  14.     while True:
  15.         event, values = window.read()
  16.         if event in (None, "退出"):
  17.             break
  18.         if event == "执行":
  19.             file_path = values[0]
  20.             sheet_name = values[1]
  21.             column_index = int(values[2])
  22.             if values["prefix_length"]:
  23.                 prefix_length = int(values[3])
  24.                 remove_prefix_from_column(file_path, sheet_name, column_index,
  25.                                           prefix_length=prefix_length)
  26.             else:
  27.                 delimiter = values[4]
  28.                 remove_prefix_from_column(file_path, sheet_name, column_index,
  29.                                           delimiter=delimiter)
  30.     window.close()
复制代码

界面层只负责收集参数,真正的处理仍然交给remove_prefix_from_column,便于后续维护和测试。

八、常见问题与排查

1. 文件打不开或内容损坏。先确认原文件不是只读状态,检查扩展名是否为.xlsx,并确认保存路径有写入权限。对于结构异常的文件,可以尝试用read_only模式读取,再用write_only模式重建工作簿。

2. 大型文件性能。处理十万行以上数据时,普通加载方式会占用较多内存。读取阶段可以使用:
  1. wb = openpyxl.load_workbook(file_path, read_only=True)
复制代码

写入阶段可以创建只写工作簿:
  1. from openpyxl import Workbook
  2. wb_new = Workbook(write_only=True)
  3. ws_new = wb_new.create_sheet()
  4. # 添加处理后的数据
  5. wb_new.save(new_file_path)
复制代码

read_only模式适合顺序读取,不能像普通模式那样直接修改单元格;write_only模式适合重建输出文件。两者通常需要配合使用,而不是在同一个工作簿对象上完成原地修改。

3. 特殊字符导致结果异常。单元格里出现换行符、制表符等空白字符时,可以在处理前统一清理:
  1. import re
  2. def clean_string(value):
  3.     if not value or not isinstance(value, str):
  4.         return value
  5.     # 替换各种空白字符为单个空格
  6.     return re.sub(r'\s+', ' ', value).strip()
复制代码

4. 多语言内容。原文提到用chardet检测编码的方式,这个示例更适合普通文本文件;xlsx本质上是包含XML的压缩包,openpyxl会按内部结构解析,通常不需要手动指定文本编码。处理中文、日文等内容时,重点应放在单元格值类型、字体显示和输出文件是否被其他程序占用上。

九、实际调用案例

采购订单号“PO-2023-10001”需要保留最后一段,可以按分隔符处理:
  1. remove_prefix_from_column("purchase_orders.xlsx", "Orders", 3, delimiter="-")
复制代码

员工工号“DEV001”“MKT002”需要删除3位部门代码,可以按固定长度处理:
  1. remove_prefix_from_column("employees.xlsx", "Sheet1", 5, prefix_length=3)
复制代码

产品SKU“SHOES-BLACK-42-1001”只需要最后的CODE部分,同样可以使用分隔符模式:
  1. remove_prefix_from_column("products.xlsx", "SKU", 2, delimiter="-")
复制代码

十、使用建议

处理前备份原始文件;首次运行时先用少量数据验证输出结果;对重要操作增加日志记录:
  1. import logging
  2. logging.basicConfig(filename='excel_processor.log', level=logging.INFO)
  3. def log_change(original, modified):
  4.     logging.info(f"Original: {original} → Modified: {modified}")
复制代码

加载文件时增加异常处理,避免单个文件损坏导致整批任务中断:
  1. try:
  2.     wb = openpyxl.load_workbook(file_path)
  3. except Exception as e:
  4.     print(f"无法加载文件 {file_path}: {str(e)}")
  5.     return False
复制代码

处理大文件时还可以按单元格数量输出进度:
  1. total_cells = len(sheet[column_letter])
  2. processed = 0
  3. for cell in sheet[column_letter]:
  4.     processed += 1
  5.     if processed % 100 == 0:
  6.         print(f"已处理 {processed}/{total_cells} 单元格...")
复制代码

按照原文的实际使用数据,处理1000行数据时,手工操作平均需要30分钟,而脚本只需2至3秒。把列定位、前缀规则和输出文件命名固定下来后,这类脚本可以复用到采购单、工号、SKU等多种表格清洗场景。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python批量删除Excel列前缀:openpyxl脚本实现与优化

这个脚本思路挺清晰的,openpyxl选型也合理,固定长度和分隔符两种模式覆盖了常见前缀清洗场景,保存为 _modified 新文件这点很稳妥,能避免误覆盖原数据。实际用的时候建议把 prefix_length 的判断改成 is not None,这样传 0 时逻辑更明确;delimiter 也最好判断非空字符串。处理前可以先确认目标列是否包含表头,必要时跳过第一行。批量跑多个文件时注意输出文件名冲突和异常捕获,最好在 finally 里关闭工作簿。整体作为可复用办公脚本已经很实用了。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python批量删除Excel列前缀:openpyxl脚本实现与优化

这个脚本思路挺清楚的,固定长度和分隔符两种模式覆盖了常见的前缀删除场景,用 get_column_letter 转列字母也很直观。保存时追加 _modified.xlsx 不覆盖原文件这点很实用,批量处理前确实要先确认输出路径。 有几个小地方可以再稳一点:判断 prefix_length 时最好用 is not None,避免传 0 时被当成没传;delimiter 也建议判断非空字符串。整列遍历会把表头也包含进去,如果表头不能被截断,可以从第二行开始处理。另外批量跑的时候输出文件名可以考虑加时间戳或放到单独目录,避免多次运行互相覆盖。整体作为可复用脚本已经很实用了。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python批量删除Excel列前缀:openpyxl脚本实现与优化

这个脚本整体思路挺清晰的,函数封装得也直观,固定长度和分隔符两种模式基本覆盖了常见的前缀清理场景。只处理非空字符串、短字符串不强行截空、保存成 _modified 文件这些细节都挺实用。提两个小提醒:一是遍历整列时如果第一行是表头,最好加个跳过表头的参数,不然表头也可能被误截;二是 prefix_length 和 delimiter 同时传入时固定长度优先,实际调用最好只传一个,避免结果不符合预期。整体很适合拿来按自己的表格结构改一改就用。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-13 05:51 , Processed in 0.025014 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部