
在整理从多个系统导出的报表时,经常需要把某些称呼统一成标准写法:把"North America"改成"America",把旧的部门名称替换成新名称,或者把"Total"统一成"Sum"。手工替换的问题在于无法留下痕迹------改完之后没人知道哪些单元格被动过,核对时只能逐行比对。更实际的做法是:让脚本完成替换,同时给每个被修改的单元格加上填充色,这样一看颜色就知道改动范围。
Python 可以通过 Spire.XLS for Python 完成这类工作。除了普通的字符串替换,它还支持正则匹配、数值查找、按格式查找,以及替换时同时应用新样式,因此可以覆盖大多数报表清洗场景。
为什么用脚本做查找替换
- 可复核:替换结果写入新文件,原文件保持不变,随时可以回退比对。
- 可标注:替换和高亮在同一次循环里完成,事后无需再排查改了哪些位置。
- 可批量:同一套规则套用到几十个工作表或几十个文件上,只需要多一层循环。
环境准备
安装 Spire.XLS:
bash
pip install Spire.XLS
在脚本中导入需要的模块:
python
from spire.xls import *
from spire.xls.common import *
查找并替换文本,同时高亮
工作表级别的 FindAllString() 返回一个包含所有匹配单元格的集合,遍历集合就能逐个修改内容并设置样式。下面这个例子把所有"Total"替换为"Sum",并将这些单元格填充为黄色:
python
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
workbook.LoadFromFile("Report.xlsx")
worksheet = workbook.Worksheets[0]
# 查找所有匹配的单元格
ranges = worksheet.FindAllString("Total", True, True)
for range in ranges:
# 改写内容,即完成替换
range.Text = "Sum"
# 填充黄色标记改动位置
range.Style.Color = Color.get_Yellow()
workbook.SaveToFile("ReplaceAndHighlight.xlsx", ExcelVersion.Version2010)
workbook.Dispose()
FindAllString() 的三个参数依次是:要查找的文本、是否区分大小写、是否整词匹配。上面传入 True, True,表示只替换大小写完全一致并且独立成词的"Total",total 或 Subtotal 不会被误伤。range.Text 既用于读取也用于写入,直接赋值即完成替换;range.Style.Color 控制单元格填充色,Color.get_Yellow() 是枚举形式的预定义颜色。
只做标记,不修改内容
在核对数据时,有时并不希望改动内容,只是想标出可疑位置。此时遍历时跳过赋值即可:
python
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
workbook.LoadFromFile("Data.xlsx")
worksheet = workbook.Worksheets[0]
ranges = worksheet.FindAllString("Area", False, False)
for range in ranges:
# 保留原文,仅用颜色标记
range.Style.Color = Color.get_Yellow()
workbook.SaveToFile("Marked.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
这里两个参数都为 False,表示忽略大小写且不要求整词匹配,因此"Area""area""Area Code"都会被标记出来。
用正则表达式匹配复杂模式
当待处理的数据没有固定字面量时,正则表达式更合适。FindAllString() 还有一个带第四个参数的重载,把该参数设为 True 即启用正则匹配:
python
from spire.xls import *
from spire.xls.common import *
workbook = Workbook()
workbook.LoadFromFile("FindTextByRegex.xlsx")
worksheet = workbook.Worksheets[0]
# 匹配所有以 North 开头的文本
ranges = worksheet.FindAllString(".*North.", False, False, True)
for range in ranges:
range.Style.Color = Color.get_Yellow()
workbook.SaveToFile("RegexHighlighted.xlsx", ExcelVersion.Version2013)
workbook.Dispose()
模式字符串遵循 .NET 正则语法,因此可以用 \d{4} 定位年份、用 ^A.* 定位以 A 开头的文本。正则模式配合高亮,常用于在一张混合了多种格式的表里快速圈出不符合规范的单元格。
查找数值与公式
文本之外,其他类型的数据也有对应的查找方法:
python
# 查找数值 100,第二个参数表示是否按整值匹配
numberRanges = sheet.FindAllNumber(100, True)
for range in numberRanges:
print(range.RangeAddress)
# 查找使用了指定公式的单元格
formulaRanges = sheet.FindAll("=SUM(A11,A12)", FindType.Formula, ExcelFindOptions.none)
for range in formulaRanges:
print(range.RangeAddress)
FindAllNumber() 的第二个参数为 True 时只匹配等于该数值的单元格,为 False 时会匹配包含该数值的单元格。FindAll() 接受 FindType 枚举,除了 Formula 之外还支持 Text、Number、DateTime 等类型;ExcelFindOptions.none 表示不使用额外匹配选项。与前面不同,这两个方法返回的集合通常用于定位------遍历时读取 range.RangeAddress 拿到单元格地址,再据此做后续处理,例如删除整行:
python
cr = sheet.FindString("Address", False, False)
sheet.DeleteRow(cr.Row)
FindString() 只返回第一个匹配项,适合"找到就处理一次"的场景。拿到结果后用 Row 属性取出所在行号,再传给 DeleteRow() 就能删掉包含该关键字的整行。
替换时同时应用新样式
如果替换不仅涉及文字,还要求更新外观,ReplaceAll() 可以在一次调用中完成内容与样式的替换。它接收四个参数:原文、原文样式、新文本、新样式:
python
newStyle = workbook.Styles.Add("newStyle")
newStyle.Font.FontName = "Arial Black"
newStyle.Font.Size = 14
cellRange = sheet.Range["D9"]
oldStyle = cellRange.Style
sheet.ReplaceAll("North America", oldStyle, "America", newStyle)
这段代码只在样式匹配 D9 单元格的区域内,把"North America"换成"America",并套用 newStyle(Arial Black、14 号)。workbook.Styles.Add() 创建的自定义样式可以反复复用,因此在处理格式统一的报表时,先用一个样本单元格取出原样式,再让 ReplaceAll() 按此样式筛选,能避免误改其他区域的同名文本。
实用建议
- 明确匹配强度 :不确定数据是否规范时先用
False, False把范围找全,确认无误后再收紧为整词匹配,避免漏改或误改。 - 记录改动地址 :在循环中把
range.RangeAddress收集到列表或日志文件,替换完成后即可生成一份改动清单。 - 先备份再覆盖:即使脚本把结果保存为新文件,也建议保留原始文件,便于比对替换前后的差异。
- 资源释放 :处理完一个工作簿后调用
workbook.Dispose();批量处理时逐个加载、处理、释放,避免内存持续增长。 - 跨工作表执行 :
FindAllString()作用于单个工作表,多表场景用Worksheets.Count配合get_Item(i)循环即可。
结语
本文介绍了使用 Python 在 Excel 中查找和替换数据的几种方式:通过 FindAllString() 完成文本替换并用 Style.Color 高亮标记,借助正则重载匹配复杂模式,用 FindAllNumber() 与 FindAll() 定位数值和公式,以及在替换的同时通过 ReplaceAll() 应用新样式。把这几种方式组合起来,就可以构建一套可复核的报表清洗流程,替换与标注一次完成。