HotXLS 文档

公式输入验证策略

经典 XLS 和 XLSX 公开相同的无副作用契约,用于在调用方更改单元格之前对公式输入进行分类

共享策略

TXLSFormulaEmptyStringMode =
  (xlfesmClearCell, xlfesmPreserveString);
TXLSFormulaUnknownSymbolMode =
  (xlfusmReject, xlfusmPreserve);

TXLSFormulaEntryPolicy = record
  EmptyStringMode: TXLSFormulaEmptyStringMode;
  UnknownFunctionMode: TXLSFormulaUnknownSymbolMode;
  UnknownNameMode: TXLSFormulaUnknownSymbolMode;
end;

property FormulaEntryPolicy: TXLSFormulaEntryPolicy;
经典 IXLSWorkbook 与 TXLSWorkbook 和 TXLSXWorkbook一同公开该属性; XLSDefaultFormulaEntryPolicy 会清空空输入,并拒绝未知函数和未知的已定义名称
工作簿属性按值复制,并在赋值时以原子方式验证,因此不受支持的枚举值会引发 EArgumentOutOfRangeException ,而不会改变先前的策略

验证结果

function ValidateFormulaEntry(Row, Col: Integer;
  const Formula: WideString): TXLSFormulaEntryValidationResult;

TXLSFormulaEntryValidationResult = record
  Issue: TXLSFormulaEntryIssue;
  Action: TXLSFormulaEntryAction;
  Symbol: WideString;
end;
Issue 区分无错误、未知函数、未知名称、语法错误、目标格式限制以及无效的目标坐标
Action 选择清空单元格、存储字符串、存储公式或拒绝,而当识别出未知函数或名称时, Symbol 会标识它

空文本与公式

完全为空的输入会依据 xlfeaClearCell 返回 xlfeaStoreString 或 EmptyStringMode
返回空字符串的公式(例如 ="")仍然是公式,并在目标格式能够表示它时返回 xlfeaStoreFormula when the target format can represent it
公式文本可以包含或省略开头的一个等号
现在通过正常的经典 XLS 或 XLSX 单元格 Value 属性赋予完全空的字符串会直接应用此模式:默认行为会移除任何已有的值或公式,而 xlfesmPreserveString 会用长度为零的常量字符串替换单元格内容
Null 和 Unassigned 总是清空单元格,而诸如 ="" 的真实公式仍是公式,并带有独立存储或计算出的空字符串结果
XLSX 的字符串和 Variant 行或列写入器应用同样的规则,且不会物化被清空的空洞,只要剩余值在该存储中可表示,就保持打包存储
原生 XLS、XLSX 和 ODS 读取器以及 XLS 转 XLSX 或 XLSX 转 XLS 转换器将存储的值视为可信输入,因此即使目标工作簿保留默认的用户输入策略,文件级常量空字符串也会保留下来

未知符号与目标格式

已注册的工作簿级或进程级用户函数以及已赋值的 OnUserFunction 或 OnUserFunctionEx 回调在验证期间视为已知,且不会调用处理程序;未注册的调用会在 Symbol
严格模式对未知函数或名称返回 xlfeaReject ;通过正常的单元格 Formula 属性赋值会在更改值、公式缓存或依赖关系之前予以拒绝
保留模式保留诊断信息并通过正常的单元格 API 存储公式;经典 XLS 通过 BIFF 加载项用户函数标记序列化未知调用,并在保存、重新打开和 XLS/XLSX 转换过程中保留未知名称文本
经典 XLS 还会通过 BIFF 写入器编译已知公式,并在表达式无法表示时返回 xlfeiTargetFormatUnsupported ;XLSX 则接受其公式模型支持的已知现代公式函数
XLSX 验证可识别 LAMBDA、词法 LET 可调用对象、引用 lambda 的已定义名称、报表占位符、已注册的外部工作簿链接,以及有效的限定或非限定结构化引用(包括嵌套形式和当前行形式)
兼容模式还可以保留未解析的外部结构化引用供后续处理,而严格模式仍会拒绝未解析的表名

计算与批量审计

在经过 lambda 和用户函数分派后仍未处理的被保留未知函数,或被保留的未知名称,会求值为代码为 $1D的 Variant 错误,即 Excel 的 #NAME? 错误;同样的类型化错误会传播到依赖公式和上下文求值中
已知但未实现的内置函数仍与未知调用有所区别;注册、替换、移除或清除工作簿级或进程级用户函数,以及更改已定义名称的公式,都会使过期的依赖关系和计算状态失效
function AuditFormulaSymbols: TXLSFormulaSymbolIssues; overload;
function AuditFormulaSymbols(
  out AStats: TXLSFormulaSymbolAuditStats):
  TXLSFormulaSymbolIssues; overload;

TXLSFormulaSymbolIssueKind = (xlfsiUnknownFunction, xlfsiUnknownName,
  xlfsiSyntaxError);
TXLSFormulaSymbolLocationKind = (xlfslCell, xlfslDefinedName);

TXLSFormulaSymbolIssue = record
  Kind: TXLSFormulaSymbolIssueKind;
  LocationKind: TXLSFormulaSymbolLocationKind;
  Sheet: Integer;
  Row: Integer;
  Col: Integer;
  DefinedNameIndex: Integer;
  DefinedName: WideString;
  Formula: WideString;
  Symbol: WideString;
end;
TXLSWorkbook and TXLSXWorkbook 提供相同的批量审计重载;每个问题都会标识未知函数、未知名称或语法错误,并同时给出工作表单元格位置(LocationKind = xlfslCell和LocationKind = xlfslDefinedName提供相同的批量审计重载;每个问题都会标识未知函数、未知名称或语法错误,并同时给出工作表单元格位置或已定义名称位置、源公式以及未解析的符号
审计遍历的是解析后的公式树或存储的 BIFF 标记,而不是扫描文本,因此字符串内类似函数的文本不会被报告;XLSX 编译使用操作本地缓存,其键由解析后的公式文本、工作表、区域设置、已定义名称的世代、用户函数注册表的世代以及回调的可用性组成
EntryCount 报告已审计的公式, UniqueFormulaCount 报告独立编译或检查的公式, CacheHitCount 报告由操作缓存服务的重复条目

不修改工作表

验证不会创建或编辑单元格,不会更改缓存的值,不会更新依赖关系图,不会将工作簿标记为已修改,也不会调用已注册的用户函数处理程序
直接验证始终保持无修改;正常的公式赋值会在内部应用同样的决策,并且只有在验证和目标格式准备成功后才提交

Delphi 示例

Policy := Workbook.FormulaEntryPolicy;
Policy.UnknownFunctionMode := xlfusmReject;
Policy.UnknownNameMode := xlfusmReject;
Workbook.FormulaEntryPolicy := Policy;

Validation := Sheet.ValidateFormulaEntry(4, 2, FormulaText);
if Validation.Action = xlfeaStoreFormula then
  Sheet.Cells[4, 2].Formula := FormulaText;

Issues := Workbook.AuditFormulaSymbols(Stats);
for I := 0 to High(Issues) do
  if Issues[I].Kind = xlfsiUnknownFunction then
    LogUnknownSymbol(Issues[I].Symbol);

另请参阅

上下文公式求值
Excel 计算引擎
附加的经典 XLS API 成员
高级 XLSX API 成员