这是我编写的一些代码,用于解析和评估 Excel 2008 xml 格式电子表格中的公式。我使用 perl 以 Excel 格式生成了一些非常复杂的报告(XLSX:Excel:Writer Using Perl to get Excel),所以我的 xlsx 输出文件没有为每个单元格预计算的值,默认情况下 excel 写入其输出的方式。为了测试和验证我推送给客户的值,我需要能够评估我编写的公式。这是一个相当长的故事,但它以使用 tcllib 1.13 中的 Tcl 解析器工具的示例开始:PEG xlsexpr (Formula) Formula <- Expr EOF ; MulOp <- '<' / '>' / '=' / '*' / '/' ; Expo <- Value ('^' Value)? ; Prod <- Expo WS (MulOp WS Expo)*; AddOp <- '^' / '+' / '-' ; Sum <- Prod WS (AddOp WS Prod)* ; Expr <- 总和;无效:WS <- <空格>* ;价值 <- UnOp? ( '(' Expr ')' / String / Func / Range / Cell / Number ) ; UnOp <- '-' / '+' ; Func <- FunName '(' WS FunArgs WS ')' ; FunName <- <alpha><alnum>* ; FunArgs <- Expr WS (',' WS Expr)* ;范围 <- 表? RowCol ':' RowCol ;单元格 <- 表?细胞_ ;工作表<-“'”?床单_ ”'”? '! ; Sheet_ <- [-A-Za-z0-9& ]+ ; Cell_ <- <alpha>+<ddigit>+ ; RowCol <- Col_ Row_ ; Col_ <- <alpha>+ ;行_ <- <ddigit>+ ;字符串 <- '"' [A-Za-z0-9]* '"' ;叶子:数字 <- 符号? ( <ddigit>+ Frac? ) / Frac? ;符号 <- '-' / '+' ;压裂 <- '.' <ddigit>* ; EOF <- !. ;结尾;这是一个生成 Excel 表达式解析器的 Makefile。我想要解析器工具来为 TclOO 生成解析器,但是 oo:: 的评估器失败了,所以我最终使用了 snit。 sed 命令修复了解析器工具输出中的一个小错误: PT=pt parser : xls-parser.peg $(PT) generate snit -class xls-parser -name xls-parser xls-parser.tcl peg xls-parser。 peg sed -es/PACKAGE/xls-parser/ < xls-parser.tcl > tmp mv tmp xls-parser.tcl 这是顶级驱动程序。这里有趣的命令是“比较”。它两次加载相同的电子表格,从任何包含一个副本的公式的单元格中清除 excel 提供的值,然后将这些值与单元格的 tcl 评估值进行比较。 #!/bin/env tclkit8.6 # 源 xml.tcl ; # 这是税源 xlsx.tcl package require vfs package require vfs::zip set argv [lassign $argv op] switch $op { form { lassign $argv file sheet cell workbook create wb $file puts [[wb name2obj $sheet] form $cell] } cell { lassign $argv file sheet cell workbook create wb $file puts [[wb name2obj $sheet] cell $cell] } cell+ { lassign $argv file sheet cell workbook create wb $file wb clear puts [[wb name2obj $sheet] 单元格? $cell] } cells { workbook create wb [lindex $argv 0] foreach name [wb sheet] { set sheet [wb name2obj $name] puts [list $sheet [$sheet cells]] } } compare { lassign $argv file1 file2 workbook create wb1 $file1 workbook create wb2 $file2 wb2 clear foreach name1 [wb1 sheet] name2 [wb2 sheet] { if { $name1 ne $name2 } { puts "Sheet names don't match $name1, $name2" } set sh1 [wb1 name2obj $name1] set sh2 [wb2 name2obj $name2] foreach cell [$sh1 cells] { #puts "$name1 $cell" if { [set v1 [$sh1 cell $cell]] != [set v2 [$ sh2 cell $cell]] } { puts "$name1 : $cell $v1 != $v2" } } } } } 这是代码的主要内容。它将近 500 行,所以我只在这里引用它:https://github.com/jbroll/xlsx-expr
包括打开 xlsx 文件并将 xml 中的单元格值、公式和格式解析为 xlsx 对象中的实例变量数组的方法。 “=”方法计算单元格的值,包括通过电子表格中的所有引用遵循公式。每个公式值仅缓存和评估一次。通过使用解析器工具将它们解析为 AST 格式,然后将 AST 作为脚本执行来评估公式。脚本的结果是一个适合 expr 的表达式,然后调用它来获取单元格的值。这里只实现了足够的东西来支持我的电子表格中的语法和函数,但扩展它应该是直接的。我已经评估了带有多个工作表的工作簿,跨越数千个单元格的复杂公式,与 excel 自行计算的值完全一致。解析器使用 tcl::chan::string 提供,它需要一个小补丁。 Allowance 方法被破坏,所以我只是在 virtchannel_core/events.tcl:Allowance 的开头添加了一个“返回”以禁用任何检查。无论如何,这种检查对我来说似乎太过分了。 AK:检查是必要的。我 2009 年关于反射和转换通道的会议论文(在 TCA 会议页面上)解释了这些内部结构。实际问题是缺少构造函数链阻止了事件管理核心的正确初始化。在源存储库头中修复。