AHK 操作 Excel,很多人第一反应是 COM。COM 的优点是功能完整,缺点也明显:机器上要装 Excel,启动慢,弹窗和进程残留也要处理。对于后台报表、数据交换、批量生成 xlsx,有时候我们并不需要打开 Excel。
XlsxUDFAHK 是我整理的 AHK v1 轻量 xlsx 读写和局部编辑库,它直接处理 xlsx 的 OOXML zip 包,不依赖 Excel COM,也不要求本机安装 Microsoft Excel。
它适合做什么
- 把 AHK 二维数组导出成 xlsx。
- 读取工作表或指定范围。
- 生成多工作表报表。
- 追加数据行。
- 修改固定单元格。
- 写入公式、布尔值、日期、富文本和基础样式。
- 给后台脚本生成导入模板、订单明细、统计报表。
最常见:数组写成 xlsx
#Requires AutoHotkey v1.1
#Include <XlsxUDFAHK>
rows := []
rows.Push(["姓名", "城市", "金额"])
rows.Push(["张三", "北京", 128.5])
rows.Push(["李四", "上海", 256])
_xlsx_WriteFromArray("report.xlsx", rows)
读取 xlsx
sheet := _xlsx_2Array("report.xlsx")
for r, row in sheet {
line := ""
for c, val in row
line .= (line = "" ? "" : " | ") . val
FileAppend, %line%`n, *
}
多工作表和常用格式
库支持列宽、行高、冻结窗格、自动筛选、合并单元格。对于后台报表来说,这些功能比复杂图表、透视表更常用。
salesSheet := {}
salesSheet["Name"] := "销售明细"
salesSheet["Rows"] := rows
salesSheet["FreezePane"] := "A2"
salesSheet["AutoFilter"] := true
workbook := []
workbook.Push(salesSheet)
_xlsx_WriteWorkbook("multi.xlsx", workbook)
局部编辑和追加数据
appendRows := []
appendRows.Push(["王五", "广州", 88])
_xlsx_AppendRows("multi.xlsx", "append.xlsx", appendRows, 1)
_xlsx_SetCell("append.xlsx", "edit.xlsx", 1, 2, "C", 999)
LibXL 风格兼容层
包里还有 XlsxUDFAHK_LibxlStyle.ahk,它不是 libxl.dll,也不依赖 LibXL,只是提供一层更接近 XL_v1 / LibXL 的写法。以前习惯 book.addSheet()、sheet["A1"] := "文本" 这类风格的人,可以用它过渡。
#Include <XlsxUDFAHK_LibxlStyle>
XL.init()
book := XL.New("xlsx")
sheet := book.addSheet("测试报告")
sheet["A1"] := "文本"
sheet[4, 7] := 543.3
book.save("libxl_style.xlsx")
边界也要知道
它不处理 xls 老格式,不执行完整 Excel 公式计算,不覆盖图表、透视表、宏、条件格式这类完整 Excel 对象模型。复杂表格排版和高级功能,仍然是 Excel COM 或专业库更合适。
这个库的价值在于:后台生成和读取 xlsx 时,不启动 Excel,脚本部署更轻,适合自动化工具批量跑。
打包库和示例的下载地址:
demo代码片段展示:
#NoEnv
#SingleInstance Force
#Include <XlsxUDFAHK>
; 使用说明:
; 1. 直接双击 demo.ahk,会在当前目录生成多个中文示例 xlsx 文件,并弹窗显示结果。
; 2. 本库不依赖 Excel COM,适合后台批量生成和读取 xlsx 数据文件。
summary := ""
singlePath := A_ScriptDir . "\示例_01_单表读写回环.xlsx"
multiPath := A_ScriptDir . "\示例_02_多工作表_列宽行高_冻结筛选.xlsx"
appendPath := A_ScriptDir . "\示例_03_追加行.xlsx"
editPath := A_ScriptDir . "\示例_04_指定单元格编辑.xlsx"
mergePath := A_ScriptDir . "\示例_05_合并单元格.xlsx"
stylePath := A_ScriptDir . "\示例_06_保留样式编辑.xlsx"
structuredPath := A_ScriptDir . "\示例_07_富文本_布尔_公式缓存_日期序列_基础样式.xlsx"
Demo_Log("XlsxUDFAHK 中文示例")
Demo_Log("")
; 这里用 AHK 数组模拟二维表格。每个子数组就是 Excel 的一行。
salesRows := []
salesRows.Push(["姓名", "城市", "金额", "日期", "备注"])
salesRows.Push(["张三", "北京", 128.5, "2026-05-22", "中文文本正常"])
salesRows.Push(["李四", "上海", 256, "2026-05-23 09:30", "日期时间会写成 Excel 日期样式"])
salesRows.Push(["王五", "广州", 88, "14:05", "时间会写成 Excel 时间样式"])
salesRows.Push(["合计", "", "=SUM(C2:C4)", "", "公式会写入单元格"])
; 生成单工作表:适合导出最常见的清单、报表、日志。
if (!Demo_Check(_xlsx_WriteFromArray(singlePath, salesRows), "生成单工作表失败"))
ExitApp, 1
Demo_Log("已生成:" . singlePath)
; 读取 xlsx:默认读取第一个工作表,返回 AHK 二维数组。
sheet := _xlsx_2Array(singlePath)
if (!Demo_Check(IsObject(sheet), "读取单工作表失败"))
ExitApp, 1
Demo_Log("")
Demo_Log("读取完整工作表:")
Demo_Log(Demo_ArrayPreview(sheet))
; 读取指定范围:第 2 到第 4 行,第 A 到 C 列。列号也可以写成数字 1 到 3。
range := _xlsx_2Array(singlePath, 1, 2, 4, "A", "C")
Demo_Log("")
Demo_Log("读取指定范围(第 2-4 行,第 A-C 列):")
Demo_Log(Demo_ArrayPreview(range))
; 多工作表生成:每个工作表可以带自己的名称、列宽、行高、冻结窗格、自动筛选。
colWidths := {}
colWidths["A"] := 12
colWidths["B"] := 12
colWidths["C"] := 10
colWidths["D"] := 18
colWidths["E"] := 28
rowHeights := {}
rowHeights[1] := 24
salesSheet := {}
salesSheet["Name"] := "销售明细"
salesSheet["Rows"] := salesRows
salesSheet["ColumnWidths"] := colWidths
salesSheet["RowHeights"] := rowHeights
salesSheet["FreezePane"] := "A2"
salesSheet["AutoFilter"] := true
salesSheet["MergeCells"] := ["A1:B1"]
summaryRows := []
summaryRows.Push(["项目", "值"])
summaryRows.Push(["总金额", "=SUM('销售明细'!C2:C4)"])
summaryRows.Push(["城市数", 3])
summaryRows.Push(["说明", "这是第二个工作表"])
summaryWidths := {}
summaryWidths["A"] := 16
summaryWidths["B"] := 24
summarySheet := {}
summarySheet["Name"] := "汇总"
summarySheet["Rows"] := summaryRows
summarySheet["ColumnWidths"] := summaryWidths
summarySheet["FreezePane"] := "A2"
workbook := []
workbook.Push(salesSheet)
workbook.Push(summarySheet)
if (!Demo_Check(_xlsx_WriteWorkbook(multiPath, workbook), "生成多工作表失败"))
ExitApp, 1
Demo_Log("")
Demo_Log("已生成:" . multiPath)
; 结构化单元格写法:用于表达普通二维数组不方便表达的 Excel 单元格能力。
; Bool 写入布尔值;Expr 写入公式;Value 可作为公式缓存值或实际值;
; RichText 写入同一单元格内的多段字体;Format 写入基础数字格式、填充色、字体色、边框、对齐。
richRuns := []
richRuns.Push({"Text": "我", "Color": 2, "Size": 20})
richRuns.Push({"Text": "爱", "Color": 3, "Size": 24})
richRuns.Push({"Text": "AHK", "Color": 4, "Size": 12, "Bold": true})
structuredRows := []
structuredRows.Push(["功能", "示例值", "说明"])
structuredRows.Push(["布尔值", {"Bool": false}, "生成 Excel 布尔单元格"])
structuredRows.Push(["公式", {"Expr": "3*4+2", "Value": 14}, "写入公式,同时写入缓存结果"])
structuredRows.Push(["富文本", {"RichText": richRuns}, "一个单元格内多段颜色和字号"])
structuredRows.Push(["日期序列", {"Value": Xlsx_DatePack(2010, 3, 11, 10, 25, 55), "Format": {"NumFmt": 22}}, "后台写 Excel 日期序列,不依赖 Excel COM"])
structuredRows.Push(["基础样式", {"Value": "重点", "Format": {"FillColor": 13, "FontColor": 2, "Bold": true, "Align": "center", "Border": true}}, "填充色、字体色、粗体、居中、边框"])
structuredRows.Push(["单元格级列宽", {"Value": "这一列更宽", "Width": 28}, "可在单元格对象上设置列宽"])
structuredSheet := {}
structuredSheet["Name"] := "结构化单元格"
structuredSheet["Rows"] := structuredRows
structuredBook := []
structuredBook.Push(structuredSheet)
if (!Demo_Check(_xlsx_WriteWorkbook(structuredPath, structuredBook, {"CalcMode": "manual"}), "生成结构化单元格示例失败"))
ExitApp, 1
Demo_Log("")
Demo_Log("已生成:" . structuredPath)
; 查询工作表列表。
sheets := _xlsx_getWorkSheets(multiPath)
Demo_Log("多工作表列表:")
for index, item in sheets
Demo_Log(item[1] . ". " . item[2])
; 追加行:适合在已有报表末尾补数据。默认走保留样式路径,只改目标工作表 XML。
appendRows := []
appendRows.Push(["赵六", "深圳", 390, "2026-05-24", "这是追加的新行"])
if (!Demo_Check(_xlsx_AppendRows(multiPath, appendPath, appendRows, 1), "追加行失败"))
ExitApp, 1
Demo_Log("")
Demo_Log("已生成:" . appendPath)
; 指定单元格编辑:行列从 1 开始,列既可以写数字,也可以写 A/B/C 这样的列名。
if (!Demo_Check(_xlsx_SetCell(appendPath, editPath, 1, 2, "E", "已通过 _xlsx_SetCell 修改备注"), "编辑指定单元格失败"))
ExitApp, 1
if (!Demo_Check(_xlsx_SetCell(editPath, editPath, 2, 4, "B", "第二个工作表也可以编辑"), "编辑第二个工作表失败"))
ExitApp, 1
Demo_Log("已生成:" . editPath)
; 合并单元格:可以在生成工作簿时用 MergeCells,也可以对已有文件追加合并区域。
if (!Demo_Check(_xlsx_MergeCells(editPath, mergePath, 1, ["C1:E1"]), "追加合并单元格失败"))
ExitApp, 1
Demo_Log("已生成:" . mergePath)
; 保留样式编辑:只改目标 sheet XML,尽量保留列宽、行高、冻结、筛选、合并区域和已有样式号。
if (!Demo_Check(_xlsx_SetCellPreserveStyle(multiPath, stylePath, 1, 2, "E", "保留样式路径修改备注"), "保留样式编辑失败"))
ExitApp, 1
styleRows := []
styleRows.Push(["孙七", "成都", 512, "2026-05-25", "保留样式路径追加"])
if (!Demo_Check(_xlsx_AppendRowsPreserveStyle(stylePath, stylePath, styleRows, 1), "保留样式追加行失败"))
ExitApp, 1
Demo_Log("已生成:" . stylePath)
editedSheet := _xlsx_2Array(editPath, 1, 1, "", "A", "E")
Demo_Log("")
Demo_Log("编辑后的第一个工作表预览:")
Demo_Log(Demo_ArrayPreview(editedSheet))
Demo_Log("")
Demo_Log("示例运行完成。")
MsgBox, 64, XlsxUDFAHK 示例完成, %summary%
ExitApp, 0
Demo_Check(ok, message) {
global summary
if (ok)
return true
Demo_Log(message . ":" . Xlsx_LastError)
MsgBox, 16, XlsxUDFAHK 示例失败, %summary%
return false
}
Demo_ArrayPreview(arr) {
text := ""
for r, row in arr {
line := ""
if (IsObject(row)) {
for c, val in row {
if (line != "")
line .= " | "
line .= val
}
}
if (line != "") {
if (text != "")
text .= "`n"
text .= line
}
}
return text
}
Demo_Log(text) {
global summary
line := text . "`n"
summary .= line
FileAppend, %line%, *
}
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。

评论(0)