作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个程序,其中用户输入一个函数,例如sin(x)+1
。我正在使用ast
尝试通过白名单组件来确定字符串是否“安全”,如this answer中所示。现在,我想分析字符串,在没有系数的情况下在系数之间添加乘号(*
)。
例如:3x
>3*x
4(x+5)
>4*(x+5)
sin(3x)(4)
>sin(3x)*(4)
(sin
is already in globals,others this would bes*i*n*(3x)*(4)
有没有有效的算法来完成这一点?我更喜欢蟒蛇式的解决方案(也就是说,不是复杂的正则表达式,不是因为它们是蟒蛇式的,而是因为我不太了解它们,想要一个我能理解的解决方案。简单的正则表达式可以。)
我非常愿意在一个条件下使用sympy
(这对于这类事情来说非常简单):安全。显然,在引擎盖下使用。我目前的(部分)解决方案非常安全。如果有人能用不可信的输入来使sympy
更安全,我也欢迎这样做。
最佳答案
regex很容易是用普通的python完成任务的最快、最干净的方法,我甚至会为您解释regex,因为regex是一个非常强大的工具,理解起来很好。
要实现您的目标,请使用以下声明:
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)
re.sub
:匹配数字或函数调用,后跟变量或括号。
((?:\d+)|(?:[a-zA-Z]\w*\(\w+\)))((?:[a-zA-Z]\w*)|\()
:第1组。注意:括号分隔一个组,这是一种子正则表达式。捕获组被编入索引以供将来参考;组也可以用修饰符重复(稍后描述)。此组与数字或函数调用匹配。
((?:\d+)|(?:[a-zA-Z]\w*\(\w+\)))
:非捕获组。在左括号后立即带有
(?:\d+)
的任何组都不会为其自身分配索引,但仍然充当模式的“部分”。例如,
?:
将匹配“abcbcbc…”等等,但您不能访问“bcbcbcbc”匹配索引。但是,如果没有这个组,写“abc+”将匹配“abcccccc…”
A(?:bc)+
:匹配任何数字一次。所有regex都将分别匹配
\d
、
\d
和
"1"
的。
"2"
:与上一个元素匹配一次或多次。在这种情况下,前一个元素是
"3"
,任意数字。在上一个示例中,“123”上的
"123"
将成功匹配“123”作为单个元素。这对我们的regex非常重要,以确保正确注册多位数。
+
:
\d
将匹配
\d+
或
|
。在这种情况下,它将“一个数字”和“一个函数调用”分开;匹配一个数字或一个函数调用。
or
:匹配函数调用。也是一个非捕获组,如
"a|b"
。
"a"
:匹配函数调用的第一个字母。因为我们只需要确保第一个字符是字母;
"b"
在技术上是一个有效的函数名,所以没有对此进行修改。
(?:[a-zA-Z]\w*\(\w+\))
:匹配任何字母数字字符或下划线。确保第一个字母后,以下字符可以是字母、数字或下划线,并且仍然作为函数名有效。
(?:\d+)
:与前一个元素匹配0次或更多次。虽然最初看起来不必要,但星形字符有效地使元素成为可选的。在这种情况下,我们修改的元素是
[a-zA-Z]
,但函数在技术上不需要任何多个字符;
A123
是有效的函数名。
\w
将与
*
匹配,使
\w
不必要。在光谱的另一端,第一个字母后面可能有任意数量的字符,这就是为什么我们需要这个修饰符。
A()
或
A
前面加上反斜杠,它就像普通字符一样使用它。
[a-zA-Z]
匹配函数的实际函数调用部分的左括号。
\w
:匹配数字、字母或下划线一次或多次。这样可以确保函数中实际上有一个参数。
\(
:like
+
,但与右括号匹配
*
:第2组。匹配变量或左括号。
\(
:匹配变量。这与我们的函数名matcher完全相同。但是,请注意,这是一个非捕获组:这很重要,因为OR检查的方式。紧接着的或将把这个组看作一个整体。如果未对其进行分组,“Last object matched”将为
\w+
,这将不足以满足我们的需要。它会说:“匹配一个字母后跟多个字母,或者一个字母后跟一个圆括号。”将此元素放入非捕获组中可以控制OR寄存器的内容。
\)
:或字符。匹配
\(
或
((?:[a-zA-Z]\w*)|\()
。
(?:[a-zA-Z]\w*)
:匹配左括号。一旦我们检查了是否有一个左括号,我们就不需要为了我们的regex检查它之外的任何东西。
\w*
。替换字符串不是真正的regex,但它仍然具有某些特殊字符。在这种情况下,
|
将插入该号码的组。所以我们的替换字符串是这样说的:“将组1放入(要么是我们的函数调用,要么是我们的数字),然后放入星号(*),然后放入第二个组(要么是变量,要么是括号)。
关于python - 在系数之间添加乘号(*),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35929640/
特别是,使用注入(inject),以下脚本, puts (1..5).inject {|x, y| x * y} 和 puts (1..5).inject(:*), 如我所料,两者都有输出 120。
我是一名优秀的程序员,十分优秀!