在正则表达式中逃避美元符号不起作用

时间:2012-12-02 23:26:17

标签: python regex

在开始之前,我知道有比正则表达式更好的方法(比如标记化器),这不是问题所在。我已经坚持使用正则表达式,它已经按照我需要的方式工作,除了一个特殊情况,这是我需要建议的。

我需要扫描一些类似JavaScript的代码,并在每个对象声明前插入new关键字。我已经知道需要这个关键字的所有对象的名称了,我知道在我开始之前它们中没有一个会在代码中有这个关键字(所以我不需要处理重复的new个字或猜测是否某事物是一个物体。例如,典型的线条可能如下所示:

foo = Bar()

我已经知道Bar是一个'类',并且需要'new'来进行对象声明。以下正则表达式可以解决问题:

for classname in allowed_classes:
    line = re.sub(r'^([^\'"]*(?:([\'"])[^\'"]*\2)*[^\'"]*)\b(%s\s*\()' % classname, r'\1new \3', line)

它就像一个魅力,甚至确保当它在一个字符串中时不要触摸classname(正则表达式的第一部分告诉它确保前面有偶数引号 - 它有点天真的,它会打破嵌套的引号,但我不需要处理这种情况)。问题是,类名也可能包含$。因此,如果allowed_classes中存在$Bar,则允许以下行:

foo = $Bar()

由于美元符号,上述正则表达式将忽略它。我想逃避它会做的伎俩,但这个逻辑似乎对上面的行没有影响,即使$Bar是其中一个类:

for classname in allowed_classes:
    line = re.sub(r'^([^\'"]*(?:([\'"])[^\'"]*\2)*[^\'"]*)\b(%s\s*\()' % re.escape(classname), r'\1new \3', line)

我也尝试使用\手动转义它,但它也没有效果。有人可以解释为什么将$转换为\$不起作用以及可以解决的问题是什么?

由于

1 个答案:

答案 0 :(得分:9)

您当前的正则表达式无效的原因是您的班级名称前面有\b\b将匹配单词边界,因此仅限于单词字符和非单词字符之间。对于字符串foo = Bar()\b将在空格和B之间匹配,但对于foo = $Bar()\b在空格和$之间无法匹配{1}}因为它们都是非单词字符。

要解决此问题,请将\b更改为(?=\b|\B\$),以下是生成的正则表达式:

for classname in allowed_classes:
    line = re.sub(r'^([^\'"]*(?:([\'"])[^\'"]*\2)*[^\'"]*)(?=\b|\B\$)(%s\s*\()' % classname, r'\1new \3', line)

使用lookahead,您可以处理以下两种情况:

  • classname不以$开头,所以我们想要在尝试匹配classname之前使用单词边界,前瞻内部的\b处理此
  • classname确实以$开头,因此如果下一个字符是我们要匹配的$。我使用了\B\$所以它只会匹配$之前的字符不是单词字符,但这可能是不必要的,因为我无法想到任何有效的JS代码,那就是这种情况