formula模块说明¶
formula模块可用于对一段文本进行关键字解析,以及进行公式(表达式)匹配和公式值计算。主要应用场景包括代码解析结构化处理(例如将html代码按标签解析为字典形式进行处理)、对一段文本进行自定义公式识别和计算处理。
算法框架说明¶
第1步:检索单个关键字¶
将keywords(公式定义,包括开始标签及结束标签信息)分解为match_list(单关键字信息清单)
将解析文本按流处理方式(引用StringStream模块)按顺序逐字符分析和处理,算法如下:
判断流当前字符是否匹配上match_list中每个关键字的第一个字符(如果有前置字符则为前置字符),如果匹配上,则将待继续匹配的结果压入compare_stack堆栈(登记已部分匹配上的信息),待流的下一个字符处理中继续向后匹配
遍历compare_stack堆栈中的所有部分匹配信息清单,根据以下结果分别处理:
流当前字符与要匹配结果的下一个预期字符不同,认定匹配失败,从compare_stack堆栈删除
流当前字符与要匹配结果的下一个预期字符相同,且不是预期的结束字符,认定可继续匹配,更新compare_stack堆栈的匹配信息,等待下一次流字符处理
流当前字符与要匹配结果的下一个预期字符相同,且是预期的结束字符,认定已匹配完成,将匹配结果存入match_result,然后从compare_stack堆栈删除
文本流处理完,得到match_result(单关键字的分析结果),该结果登记登记了每个关键字的开始位置(start_pos)、结束位置(end_pos)、前置字符(front_char)、后置字符(end_char)等信息;注意:这个结果可能存在同一部分文本被多个关键字共同匹配上的情况
第2步:单个关键字结果的处理¶
如果检索参数不允许多重匹配(multiple_match,即同一段文本有多个关键字命中),则按照关键字获取顺序优先级参数(EnumFormulaSearchSortOrder)进行结果的处理,将关键字结果有位置重叠的情况删除多余的匹配关键字信息
对匹配结果按位置先后的顺序进行排序,生成匹配结果按顺序排序的list清单
第3步:根据单关键字结果解析公式(表达式)¶
根据公式定义清单,从match_result按位置顺序找公式开始标签及结束标签,以此获取到一个完整的公式,更新至公式解析对象(StructFormula)中
查到到公式中间部分的单关键字匹配结果采用递归算法,由其父公式发起检索匹配,更新至公式解析对象(StructFormula)中
最终获得完整的公式解析对象(StructFormula)
第4步:根据公式解析对象计算公式值¶
采用递归算法,从第1个公式开始,逐级向下找到子公式,调用对应公式关键字的计算算法函数(deal_fun)计算公式的值,并更新到公式解析对象(StructFormula)中
FormulaTool使用参考¶
静态工具¶
FormulaTool.search¶
可直接调用类的该静态方法,匹配获取单关键字信息清单(match_list)的匹配结果(match_result)
match_list的定义 @see FormulaTool/match_list
dict格式的match_result 参考 @see FormulaTool/match_result
参考代码如下:
# 尝试解析SQL语句的关键字
_source_str = 'select * From test where t.name \tlike \'%fromxxx\' order by name'
_split_common = ('\\^', '\r', '\n', ' ', '\t', '\\$') # 关键字前置及后置字符
_match_list = {
'select': (_split_common, _split_common),
'from': (_split_common, _split_common),
'where': (_split_common, _split_common),
'like': (_split_common, _split_common),
'order': (_split_common, _split_common),
'by': (_split_common, _split_common)
}
# 解析关键字
_match_result = FormulaTool.search(source_str=_source_str, match_list=_match_list, ignore_case=True,multiple_match=False, sort_oder=EnumFormulaSearchSortOrder.ListDesc)
FormulaTool.match_result_to_sorted_list¶
将dict格式的匹配结果(match_result)转换为已排序后的list格式
_match_result_list = FormulaTool.match_result_to_sorted_list(_match_result)
FormulaTool.analyse_formula¶
直接按keyworks参数解析公式文本,形成结构化字典的公式对象 @see StructFormula,该对象通过子公式的方式递归展示所有的公式信息。
示例如下:
# 解析带公式的字符串
_source_str = '[full begin] formula {$PY=[PY1 begin] xxxx{$single=$}xx{$PY=[PY2 begin]eeeee[PY2 end]$}x [PY1 end]$} from {$end=[End begin]abc {$abc=[abc begin]"[string begin]kkkaf{$PY=not formula$}dfdf,\\",""haha[string end]"[abc end]$} PY=eeffff [full end]'
# 定义字符串公式的公共关键字参数,例如python中的""引起来的认为是字符串
_string_para = StructFormulaKeywordPara()
_string_para.is_string = True # 声明是字符串参数
_string_para.has_sub_formula = False # 声明公式中不会有子公式
# 在查找字符串结束关键字时忽略的转义情况,例如"this is a string ,ignore \" , this is real end"
_string_para.string_ignore_chars = ['\\"', '""']
# 定义单关键字公式的公共参数(没有结束关键字)
_single_para = StructFormulaKeywordPara()
_single_para.is_single_tag = True # 声明是单标签公式关键字
# 定义以字符串结尾为结束标签的公共参数
_end_para = StructFormulaKeywordPara()
_end_para.end_tags = ['\\$']
# 定义公式解析的关键字参数
_keywords = {
# 第一个定义了字符串的公式匹配参数
'String': [
['"', list(), list()], # 公式开始标签
['"', list(), list()], # 公式结束标签
_string_para # 公式检索参数
],
'PY': [
['{$PY=', list(), list()], # 公式开始标签
['$}', list(), list()], # 公式结束标签
StructFormulaKeywordPara() # 公式检索参数
],
'abc': [
['{$abc=', list(), list()],
['$}', list(), list()],
StructFormulaKeywordPara()
],
'Single': [
['{$single=$}', list(), list()],
None,
_single_para
],
'End': [
['{$end=', list(), list()],
None,
_end_para
]
}
# 解析公式
_formula = FormulaTool.analyse_formula(formula_str=_source_str, keywords=_keywords, ignore_case=False)
解析并执行公式计算¶
如果需要执行公式计算,则需实例化FormulaTool类才能处理,具体步骤如下:
1、准备keyworks公式参数、公式标签对应的处理函数
2、实例化FormulaTool类
3、执行公式计算
示例如下:
# 要解析的公式
_source_str = '[开始] {$PY=10 + 21$} {$PY=\'[PY1开始]{$ab=[ab开始]testab[时间开始]{$single=$}[时间结束][ab结束]$}} [PY1结束]\'$} "[string 开始]{$PY=string py$} [string 结束]" [结束]'
# 定义字符串公式的公共关键字参数,例如python中的""引起来的认为是字符串
_string_para = StructFormulaKeywordPara()
_string_para.is_string = True # 声明是字符串参数
_string_para.has_sub_formula = False # 声明公式中不会有子公式
# 在查找字符串结束关键字时忽略的转义情况,例如"this is a string ,ignore \" , this is real end"
_string_para.string_ignore_chars = ['\\"', '""']
# 定义单关键字公式的公共参数(没有结束关键字)
_single_para = StructFormulaKeywordPara()
_single_para.is_single_tag = True # 声明是单标签公式关键字
# 定义公式解析的关键字参数
_keywords = {
# 第一个定义了字符串的公式匹配参数
'String': [
['"', list(), list()], # 公式开始标签
['"', list(), list()], # 公式结束标签
_string_para # 公式检索参数
],
'PY': [
['{$PY=', list(), list()], # 公式开始标签
['$}', list(), list()], # 公式结束标签
StructFormulaKeywordPara() # 公式检索参数
],
'ab': [
['{$ab=', list(), list()],
['$}', list(), list()],
StructFormulaKeywordPara()
],
'Single': [
['{$single=$}', list(), list()],
None,
_single_para
]
}
# 定义公式对象处理函数
_deal_fun_list = {
'PY': FormulaTool.default_deal_fun_python, # 执行python语句
'String': FormulaTool.default_deal_fun_string_content, # 只保留标签内容
'ab': formula_deal_fun_test, # 自定义公式处理函数
'Single': FormulaTool.default_deal_fun_datetime_str # 获取日期
}
# 初始化公式类
_formula_obj = FormulaTool(
keywords=_keywords,
ignore_case=False,
deal_fun_list=_deal_fun_list,
default_deal_fun=None
)
# 计算公式,所有结果转换为字符串
_formula = _formula_obj.run_formula_as_string(_source_str)
# 打印公式执行结果
print(_formula.formula_value)
自定义公式处理函数¶
可按照以下格式自定义公式的处理函数:
fun(formular_obj, **kwargs):
formular_obj : StructFormula 要处理公式对象(函数直接修改对象),该函数需更新对象的formula_value
kwargs :计算公式所传入的key=value格式的参数,参数key由处理函数定义(建议统一定义便于简化处理,由run_formula_as_string函数的kwargs参数传入)
公式处理类FormulaTool已经定义了几个默认的处理函数:
default_deal_fun_string_full :将标签自身的字符串作为设置值
default_deal_fun_string_content :将标签内容的字符串作为设置值
default_deal_fun_python :标签内容作为python代码执行,将执行结果的对象作为设置值
default_deal_fun_datetime_str :获取当前时间日期字符格式
参数详细说明¶
keywords - {dict} - 公式关键字定义,格式如下:
key - string 关键字标识名
value - list 匹配定义数组,按顺序定义为:
开始标签 - [string-匹配字符串, list-前置字符, list-后置字符]
结束标签 - [string-匹配字符串, list-前置字符, list-后置字符],结束标签可以置None(表示使用匹配参数)
匹配参数 - StructFormulaKeywordPara, 对象属性为:
object.is_single_tag : bool 该标签是否单独一个标识,不含公式内容
object.has_sub_formula : bool 是否包含子公式,如果为True则代表继续分解公式里面的子公式
object.is_string : bool 是否字符串,如果为True代表是字符串(字符串不包含子公式)
object.string_ignore_chars : list 字符串的结束标签忽略字符,例如["\\'", "''"]
object.end_tags : list 当结束标签为None时,且不是单独标签,通过该参数获取结束标识(可以为多个字符):
\$ : 以结尾为结束标签'\\$'
\t : 以下一个标签开始为当前结束标签'\\t',注意不是代表tab的'\t'