解析excel样式公式

arc*_*ght 9 python

我正在使用python从电子表格xml构建公式参考图.公式就好

=IF(AND(LEN(R[-2]C[-1])>0,R[-1]C),WriteCurve(OFFSET(R16C6, 0,0,R9C7,R10C7),R15C6,R10C3, R8C3),"NONE")
Run Code Online (Sandbox Code Playgroud)

我只对获得writecurve函数的第n个参数感兴趣.在这里我出现了非常C风格的程序,主要是计算不在括号内的昏迷.有很多嵌套公式

def parseArguments(t, func, n):
start=t.find(func)+len(func)+1
bracket = 0
ss = t[start:]
lastcomma = 0
for i, a in enumerate(ss):
    if a=="(":
        bracket +=1
    elif a==")":
        if bracket==0:
            break
        bracket-=1
    elif a == ",":
        if bracket==0 and n==0:
            break
        elif bracket ==0:
            if n-1==0:
                lastcomma = i
            n-=1
if lastcomma == 0:
    return ss[:i]
else:
    return ss[lastcomma+1:i]
Run Code Online (Sandbox Code Playgroud)

有这样做的pythonic方式吗?还是有更好的递归方式来解析整个公式?非常感谢

eca*_*mur 13

我所知道的最好的Excel公式解析器是EW Bachtal的算法.Robin Macharg有一个Python端口; 我所知道的最新版本是pycel项目的一部分,但它可以单独使用 - tokenizer.解析您的公式没有问题:

from tokenizer import shunting_yard
rpn = shunting_yard('=IF(AND(LEN(R[-2]C[-1])>0,R[-1]C),WriteCurve(OFFSET(R16C6, 0,0,R9C7,R10C7),R15C6,R10C3, R8C3),"NONE")')
print(rpn)
deque([<tokenizer.RangeNode object at 0x2b7b1f5d7850>, <tokenizer.FunctionNode object at 0x2b7b1f5d7950>, <tokenizer.ASTNode object at 0x2b7b1f5d7990>, <tokenizer.ASTNode object at 0x2b7b1f5d79d0>, <tokenizer.RangeNode object at 0x2b7b1f5d7a10>, <tokenizer.FunctionNode object at 0x2b7b1f5d7a50>, <tokenizer.RangeNode object at 0x2b7b1f5d7a90>, <tokenizer.ASTNode object at 0x2b7b1f5d7ad0>, <tokenizer.ASTNode object at 0x2b7b1f5d7b10>, <tokenizer.RangeNode object at 0x2b7b1f5d7b50>, <tokenizer.RangeNode object at 0x2b7b1f5d7b90>, <tokenizer.FunctionNode object at 0x2b7b1f5d7bd0>, <tokenizer.RangeNode object at 0x2b7b1f5d7c10>, <tokenizer.RangeNode object at 0x2b7b22efc450>, <tokenizer.RangeNode object at 0x2b7b22efc510>, <tokenizer.FunctionNode object at 0x2b7b22efc410>, <tokenizer.ASTNode object at 0x2b7b22eff110>, <tokenizer.FunctionNode object at 0x2b7b22eff150>])
Run Code Online (Sandbox Code Playgroud)

令牌器为您提供RPN堆栈; 如果您发现使用AST更方便,您可以轻松转换为AST:

def rpn_to_ast(rpn):
    stack = []
    for n in rpn:
        num_args = (2 if n.token.ttype == "operator-infix" else
                    1 if n.token.ttype.startswith('operator') else
                    n.num_args if n.token.ttype == 'function' else 0)
        n.args = [stack.pop() for _ in range(num_args)][::-1]
        stack.append(n)
    return stack[0]
Run Code Online (Sandbox Code Playgroud)

然后,您可以遍历AST以查找WriteCurve节点并检查其参数:

def walk(ast):
    yield ast
    for arg in getattr(ast, 'args', []):
        for node in walk(arg):
            yield node

write_curve = next(node for node in walk(rpn_to_ast(rpn)) if node.token.ttype == 'function' and node.token.tvalue == 'WriteCurve')
print(write_curve.args[2].token.tvalue)
R10C3
Run Code Online (Sandbox Code Playgroud)