在系数之间添加乘号(*)

Luk*_*lor 10 python string function sympy

我有一个用户输入功能的程序,例如sin(x)+1.我正在ast尝试通过将组件列入白名单来确定字符串是否"安全",如本答案中所示.现在我想解析字符串,*在没有它们的系数之间添加乘法()符号.

例如:

  • 3x- > 3*x
  • 4(x+5) - > 4*(x+5)
  • sin(3x)(4)- > sin(3x)*(4)(sin已经在全局变量中,否则这将是s*i*n*(3x)*(4)

有没有有效的算法来实现这一目标?我更喜欢pythonic解决方案(即不是复杂的正则表达式,不是因为它们是pythonic,而是因为我不理解它们并想要一个我能理解的解决方案.简单的正则表达式是可以的.)

sympy在一个条件下,我非常愿意使用(这对于这类事情看起来很容易):安全.显然是在引擎盖下sympy使用eval.我目前的(部分)解决方案的安全性非常好.如果有人有办法sympy使用不受信任的输入更安全,我也欢迎这一点.

Mut*_*pus 7

一个正则表达式是很容易获得的香草蟒蛇完成任务的最快和最干净的方式,我甚至会解释的正则表达式你,因为正则表达式是这样一个强大的工具,这是很好理解的.

要完成目标,请使用以下语句:

import re
# <code goes here, set 'thefunction' variable to be the string you're parsing>
re.sub(r"((?:\d+)|(?:[a-zA-Z]\w*\(\w+\)))((?:[a-zA-Z]\w*)|\()", r"\1*\2", thefunction)
Run Code Online (Sandbox Code Playgroud)

我知道这有点长而且复杂,但是一个不同的,更简单的解决方案并没有立即明显,没有比这里的正则表达式更多的hacky东西.但是,这已针对您的所有三个测试用例进行了测试,并且可以按照您的需要进行测试.

作为对此处发生的事情的简要说明:第一个参数re.sub是正则表达式,它匹配某个模式.第二个是我们用它替换它的东西,第三个是替换它的实际字符串.每次我们的正则表达式看到匹配时,它会删除它并插入替换,带有一些特殊的幕后技巧.

下面对正则表达式进行更深入的分析:

  • ((?:\d+)|(?:[a-zA-Z]\w*\(\w+\)))((?:[a-zA-Z]\w*)|\() :匹配数字或函数调用,后跟变量或括号.
    • ((?:\d+)|(?:[a-zA-Z]\w*\(\w+\))):第1组.注意:括号分隔一个组,这是一个子正则表达式.捕获组被索引以供将来参考; 也可以使用修饰符重复组(稍后描述).该组匹配数字或函数调用.
      • (?:\d+):非捕获组.?:紧接在左括号之后的任何组都不会为自己分配索引,但仍然充当模式的"部分".防爆.A(?:bc)+将匹配"Abcbcbcbc ..."等,但您无法使用索引访问"bcbcbccc"匹配.但是,如果没有这个小组,写"Abc +"将匹配"Abcccccccc ......"
        • \d:匹配任何数字一次.的正则表达式\d自己的所有匹配,单独,"1","2",和"3""123".
        • +:匹配前一个元素一次或多次.在这种情况下,前一个元素是\d任何数字.在前面的示例中,\d+"123"将成功匹配"123"作为单个元素.这对我们的正则表达式至关重要,以确保正确注册多位数字.
      • |:管道字符,在正则表达式中,它有效地说or:"a|b"将匹配"a"OR "b".在这种情况下,它将"数字"和"函数调用"分开; 匹配数字或函数调用.
      • (?:[a-zA-Z]\w*\(\w+\)):匹配函数调用.也是一个非捕获组,如(?:\d+).
        • [a-zA-Z]:匹配函数调用的第一个字母.这没有修饰符,因为我们只需要确保第一个字符是一个字母; A123在技​​术上是一个有效的函数名称.
        • \w:匹配任何字母数字字符或下划线.确保第一个字母后,以下字符可以是字母,数字或下划线,仍然可以作为函数名称有效.
        • *:匹配前一个元素0或更多次.虽然最初看似不必要,但明星角色有效地使元素成为可选元素.在这种情况下,我们的修改元素是\w,但函数在技术上不需要任何多于一个字符; A()是一个有效的函数名称.A将被匹配[a-zA-Z],做出\w不必要的.在光谱的另一端,第一个字母后面可能有任意数量的字符,这就是我们需要这个修饰符的原因.
        • \(:这一点很重要:这不是另一个群体.这里的反斜杠很像普通字符串中的转义字符.在正则表达式中,只要你在特殊字符(如括号+)或*反斜杠前面加上它,就会像普通字符一样使用它.\(匹配一个左括号,用于函数的实际函数调用部分.
        • \w+:匹配数字,字母或下划线一次或多次.这确保了函数实际上有一个参数进入它.
        • \):喜欢\(,但匹配一个括号
    • ((?:[a-zA-Z]\w*)|\():第2组.匹配变量或左括号.
      • (?:[a-zA-Z]\w*):匹配变量.这与我们的函数名称匹配器完全相同.但请注意,这是在非捕获组中:这很重要,因为OR检查的方式.紧随其后的OR整体上看这个组.如果没有分组,那么"匹配的最后一个对象"就是\w*,这对我们想要的东西来说是不够的.它会说:"匹配一个字母后跟更多字母或一个字母后跟括号".将此元素放在非捕获组中允许我们控制OR注册的内容.
      • |:或者角色.匹配(?:[a-zA-Z]\w*)\(.
      • \(:匹配左括号.一旦我们检查了是否有一个左括号,我们就不需要为了我们的正则表达式而检查它之外的任何东西.

现在,还记得我们的两个小组,第一组和第二组吗?这些用在替换字符串中"\1*\2".替换字符串不是真正的正则表达式,但它仍然具有某些特殊字符.在这种情况下,\<number>将插入该号码的组.所以我们的替换字符串是:"将组1放入(这是我们的函数调用或我们的数字),然后放入星号(*),然后放入我们的第二组(变量或括号)"

我认为总结一下!