马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PBIDEA:用 uo_xlsx 不装 Excel 也能读写 xlsx 文件
阅读说明
1. 适用版本:PB 9 及以上(本文按 PB 12.5 + PBIDEA 1.x 验证;涉及 websuite.pbl 一个库)
2. 支持数据库:无需数据库(示例全程读写本地 xlsx 文件,不连接任何 DBMS)
3. 操作系统与环境要求:Windows 7+,已安装 PBIDEA 运行库(PbIdea.dll 等);应用库需包含 websuite.pbl
4. 难度系数:★★☆☆☆
5. 其它阅读说明:前置知识为 PowerScript 基本语法与对象 create/destroy;建议先看 Day 7《PBIDEA:uo_database 连接池与多库事务复用》了解 PBIDEA 库的引入方式
一、uo_xlsx 是什么
很多老系统导出报表还在拼 CSV、或者调用 Excel COM(OLE),机器上没装 Office 就直接报错。PBIDEA 的 websuite.pbl 里封装了 libxl 引擎,提供一组 uo_xl_* 对象,不依赖 Excel、不依赖任何 ActiveX/OCX,就能直接读写 .xlsx(也支持老 .xls)文件。
对象关系一句话:uo_xlsx 继承自 uo_xl_book(工作簿),工作簿里装 uo_xl_sheet(工作表),工作表里是 uo_xl_cell(单元格)。uo_xlsx 的 constructor 会自动执行 xlInitXL() 和 xlCreate(2) 完成引擎初始化(2 表示 xlsx 格式;想要旧版 xls 用 uo_xls 即可,它内部是 xlCreate(1)),所以创建后直接就能用,不需要任何手动初始化。
常用能力一览(均已在本机导出源码核实):
| 对象 | 常用成员 | 说明 | | uo_xl_book(uo_xlsx) | Open(file) / save(file) | 打开 / 保存文件,返回 boolean | | SheetCount() / GetSheet(索引或名称) / InsertSheet(索引, 名称) | 工作表的数量、获取、插入 | | CreateFormat() / CreateFont() | 新建格式 / 字体对象 | | GetErrorMessage() | 取最近一次操作的错误信息 | | uo_xl_sheet | GetCell(行, 列) | 取单元格(行列都从 1 开始) | | SetCol(列, 宽度) / SetRow(行, 高度) | 设列宽 / 行高 | | FirstRow() / LastRow() / FirstCol() / LastCol() | 有数据的区域边界 | | uo_xl_cell | text / num / bool / formula | 单元格文本 / 数字 / 布尔 / 公式 | | row / col / format / celltype | 位置 / 格式 / 类型 |
celltype 判断单元格类型,常量定义在 uo_xl_base 里:CELLTYPE_EMPTY=0、CELLTYPE_NUMBER=1、CELLTYPE_STRING=2、CELLTYPE_BOOLEAN=3、CELLTYPE_STRICTDATE=6。
二、示例 1:创建工作簿,写入一行报表并保存
先看最核心的流程:新建 → 取/建工作表 → 写单元格 → 保存。写完用 save() 落盘,返回 true 表示成功。
前置:① 应用库列表已加入 websuite.pbl;② 程序运行目录可写。
步骤:1) 打开窗口 w_demo;2) 在按钮 cb_write 的 clicked 事件贴入下方代码;3) 运行并点击按钮,程序目录下生成 report.xlsx,用任意表格软件打开即可看到内容。 - // ===== 示例输入:保存路径与工作表名 =====
- string ls_file
- ls_file = 'D:/report.xlsx' // 目标文件路径(正斜杠即可)
- string ls_sheet
- ls_sheet = '销售汇总' // 工作表名称
- // ===== 创建 xlsx 工作簿(constructor 自动初始化引擎)=====
- uo_xlsx book
- book = create uo_xlsx
- // 默认自带 1 个空工作表,直接取它并改名;SheetCount 从 1 开始
- uo_xl_sheet sheet
- sheet = book.GetSheet(1)
- sheet.name = ls_sheet
- // ===== 写表头(第 1 行)=====
- uo_xl_cell cell
- cell = sheet.GetCell(1, 1)
- cell.text = '月份'
- cell = sheet.GetCell(1, 2)
- cell.text = '销售额'
- // ===== 写 3 行数据(第 2~4 行;for 变量先声明,不内联)=====
- long ll_r
- for ll_r = 1 to 3
- cell = sheet.GetCell(ll_r + 1, 1)
- cell.text = string(ll_r) + '月' // 文本单元格
- cell = sheet.GetCell(ll_r + 1, 2)
- cell.num = ll_r * 10000 // 数字单元格
- next
- // ===== 保存 =====
- boolean lb_ok
- lb_ok = book.save(ls_file)
- if lb_ok then
- MessageBox('保存成功', '文件已生成:' + ls_file + '(共 ' + string(book.SheetCount()) + ' 个工作表)')
- else
- MessageBox('保存失败', '错误:' + book.GetErrorMessage())
- end if
- destroy book
复制代码
要点:GetCell(行, 列) 的行列都从 1 起;cell.text 写文本、cell.num 写数字;book.save() 成功后文件立即可用。注意 destroy book 一定要写——uo_xlsx 的 destructor 会调用 xlDestroy() 释放 libxl 资源,不销毁会在多次创建时泄漏。
三、示例 2:打开已有文件,读出单元格内容
读取方向和写完全对称:Open() 打开文件,GetSheet() 取工作表,GetCell() 取单元格,用 text / num 读值,用 celltype 判断类型。
前置:同示例 1;先用示例 1 生成 D:/report.xlsx(或准备任意 xlsx)。
步骤:在按钮 cb_read 的 clicked 事件贴入下方代码;运行后点击按钮,MessageBox 依次显示读到的表头和数据。 - // ===== 示例输入:要读取的文件路径 =====
- string ls_file
- ls_file = 'D:/report.xlsx' // 已存在的 xlsx 文件
- uo_xlsx book
- book = create uo_xlsx
- boolean lb_ok
- lb_ok = book.Open(ls_file)
- if not lb_ok then
- MessageBox('打开失败', '错误:' + book.GetErrorMessage())
- destroy book
- return
- end if
- // 取第一个工作表
- uo_xl_sheet sheet
- sheet = book.GetSheet(1)
- // 读第 1 行两个表头(text 属性取文本;取不到时返回空串)
- uo_xl_cell cell
- cell = sheet.GetCell(1, 1)
- string ls_c1
- ls_c1 = cell.text
- cell = sheet.GetCell(1, 2)
- string ls_c2
- ls_c2 = cell.text
- // 读第 2 行两个数据(num 属性取数字;同时打印 celltype 判断类型)
- cell = sheet.GetCell(2, 1)
- string ls_r2c1
- ls_r2c1 = cell.text
- cell = sheet.GetCell(2, 2)
- double ld_r2c2
- ld_r2c2 = cell.num
- int li_type
- li_type = cell.celltype
- string ls_msg
- ls_msg = '表头: ' + ls_c1 + ' / ' + ls_c2 + '~r~n' + '第2行: ' + ls_r2c1 + ' / ' + string(ld_r2c2) + '~r~n' + '第2行第2列类型: ' + string(li_type) + '(1=数字)'
- MessageBox('读取结果', ls_msg)
- destroy book
复制代码
补充一句:判断有没有数据,最省事的是 sheet.LastRow() / sheet.LastCol()——只含表头时 LastRow() 返回 1,循环输出报表时常用它做行数上限。
四、示例 3:给表头加粗、设列宽
导出给领导看的报表,表头加粗、列宽合适才像样。这要用到 uo_xl_format(格式)和 uo_xl_font(字体):CreateFormat() 新建格式 → CreateFont() 新建字体 → font.bold = true 加粗 → format.font = font 把字体挂到格式上 → cell.format = format 把格式赋给单元格;列宽用 sheet.SetCol(列, 宽度)。
前置:同示例 1。步骤:在按钮 cb_format 的 clicked 事件贴入下方代码;运行后点击按钮,生成的 report_fmt.xlsx 表头为加粗字体、A 列宽 10、B 列宽 16。 - // ===== 示例输入 =====
- string ls_file
- ls_file = 'D:/report_fmt.xlsx'
- uo_xlsx book
- book = create uo_xlsx
- uo_xl_sheet sheet
- sheet = book.GetSheet(1)
- // 写表头
- uo_xl_cell cell
- cell = sheet.GetCell(1, 1)
- cell.text = '月份'
- cell = sheet.GetCell(1, 2)
- cell.text = '销售额'
- // ===== 新建格式:字体加粗、水平居中 =====
- uo_xl_format fmt
- fmt = book.CreateFormat()
- uo_xl_font fnt
- fnt = book.CreateFont()
- fnt.bold = true
- fmt.font = fnt
- fmt.alignH = 2 // ALIGNH_CENTER=2,居中
- // 把格式赋给表头两个单元格
- cell = sheet.GetCell(1, 1)
- cell.format = fmt
- cell = sheet.GetCell(1, 2)
- cell.format = fmt
- // ===== 设列宽:A 列 10,B 列 16 =====
- sheet.SetCol(1, 10.0)
- sheet.SetCol(2, 16.0)
- boolean lb_ok
- lb_ok = book.save(ls_file)
- if lb_ok then
- MessageBox('保存成功', '已生成带格式的报表:' + ls_file)
- else
- MessageBox('保存失败', '错误:' + book.GetErrorMessage())
- end if
- destroy book
复制代码
uo_xl_base 里还预置了大量常量,比如对齐方式 ALIGNH_LEFT=1 / ALIGNH_CENTER=2 / ALIGNH_RIGHT=3、边框 BORDERSTYLE_THIN=1、填充 FILLPATTERN_SOLID=1 等,写表格时直接引用,不必记数字。
五、常见坑
- 忘了 destroy:uo_xlsx 释放时要调 xlDestroy(),不 destroy book 会泄漏 libxl 资源,程序里反复创建会越来越慢;
- 行列从 1 开始:GetCell(1,1) 是 A1,不是 (0,0);
- 数字 vs 文本:写数字用 cell.num,写文本用 cell.text——把数字当文本写进去,Excel 里会带绿色三角提示、无法求和;
- 保存失败先看错误:路径不存在、文件被 Excel 占用都会让 save() 返回 false,此时 book.GetErrorMessage() 会给具体原因;
- 旧版 .xls 文件:uo_xlsx 内部是 xlCreate(2)(xlsx 格式),处理老 .xls 用 uo_xls 对象,用法完全一致。
小结
uo_xlsx 的核心流程只有四步:create 工作簿 → GetSheet 取工作表 → GetCell 写/读单元格 → save/Open 保存或打开;要好看再加一步 CreateFormat 配格式。整个读写过程不依赖本机安装 Excel,服务器上跑批导出也毫无压力。下一篇继续 PBIDEA 的线程组件:用 uo_thread 把耗时操作丢到后台,避免界面卡死。 |