使用regex从代码语句中提取变量

时间:2015-10-25 15:56:16

标签: java regex pattern-matching

我试图从代码语句中提取变量"如果"条件。我有一个正则表达式,但mymatcher.find()没有返回任何匹配的值。 我不知道出了什么问题。

这是我的代码:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class test {
    public static void main(String[] args) {
        String test="x=y+z/n-10+my5th_integer+201";
        Pattern mypattern = Pattern.compile("^[a-zA-Z_$][a-zA-Z_$0-9]*$");
        Matcher mymatcher = mypattern.matcher(test);    
        while (mymatcher.find()) {
            String find = mymatcher.group(1) ;
            System.out.println("variable:" + find);
        }
    }
}

2 个答案:

答案 0 :(得分:3)

您需要删除分别在字符串开头和结尾处断言位置的^$个锚点,并使用mymatcher.group(0)代替mymatcher.group(1),因为您没有捕获正则表达式中的组:

String test="x=y+z/n-10+my5th_integer+201";
Pattern mypattern = Pattern.compile("[a-zA-Z_$][a-zA-Z_$0-9]*");
Matcher mymatcher = mypattern.matcher(test);    
while (mymatcher.find()) {
    String find = mymatcher.group(0) ;
    System.out.println("variable:" + find);
}

请参阅IDEONE demo,结果如下:

variable:x
variable:y
variable:z
variable:n
variable:my5th_integer

答案 1 :(得分:2)

通常仅使用正则表达式处理源代码就会失败。

如果您要做的只是选择标识符(我们将在下面进一步讨论变量),您就有机会使用正则表达式(毕竟,这是lexers的用法)建)。

但是你可能需要比你拥有的版本更复杂的版本,即使是其他作者建议的更正。

第一个问题是,如果您允许任意语句,它们通常具有看起来像标识符的关键字。在您的具体示例中,“if”看起来像一个标识符。因此,您的匹配器必须识别类似标识符的子字符串,并减去已知的关键字,或者正则表达式本身必须表达标识符具有基本形状但不能看起来像特定关键字列表的想法。 (后者称为减法正则表达式,在大多数正则表达式引擎中都找不到。它看起来像:

 [a-zA-Z_$][a-zA-Z_$0-9]* - (if | else | class | ... )

我们的DMS词法分析器生成器[见我的生物]具有减法正则表达式,因为这在语言中非常有用。)

如果“关键字”并不总是关键字,那就会变得更复杂,也就是说, 它们只能在某些情况下成为关键词。 Java“关键字”枚举就是这样:如果你在类型上下文中使用它,它就是一个关键字;否则它是一个标识符; C#很相似。现在唯一的方法就是知道 如果声称的标识符是关键字,则实际解析代码(这是您检测控制其关键字的上下文的方式)。

接下来,Java中的标识符允许各种Unicode字符(Latin1,俄语,中文,...)正则表达式识别这一点,占所有字符,比你提出的简单“AZ”样式大很多

对于Java,您需要防范包含看似变量名称的字符串文字。考虑(看起来很有趣但有效)的陈述:

a =  "x=y+z/n-10+my5th_integer+201";

此处只有一个标识符。注释也会出现类似的问题 包含看起来像语句的内容:

/* Tricky:
   a =  "x=y+z/n-10+my5th_integer+201";
*/

对于Java,您还需要担心Unicode转义。请考虑这个有效的Java语句:

\u0061 = \u0062; //  means  "a=b;"

或更糟糕:

a\u006bc = 1; //  means "akc=1;" not "abc=1;"!

推送它,没有Unicode字符解码,你甚至可能不会 注意一个字符串。以下是上述的变体:

a =  \u0042x=y+z/n-10+my5th_integer+201";

要正确提取标识符,您需要构建(或使用)完整Java词法分析器的等效项,而不仅仅是简单的正则表达式匹配。

如果你不关心大多数时候是正确的,你可以试试你的正则表达式。通常,正则表达式应用于源代码解析的结果很糟糕,部分原因是由于上述问题(例如,过度简化)。

你很幸运,因为你正在尝试为Java做。如果必须为C#(一种非常相似的语言)执行此操作,则必须处理插值字符串,这允许在字符串内部使用表达式。表达式本身可以包含字符串......它的乌龟一直在下降。考虑C#(版本6)声明:

a  = $"x+{y*$"z=${c /* p=q */}"[2]}*q" + b;

这包含标识符a,b,c和y。每个其他“标识符”实际上只是一个字符串或注释字符。 PHP有类似的插值字符串。

要从中提取标识符,您需要一些能够理解字符串元素嵌套的东西。 Lexers通常不进行递归(我们的DMS词法分析器处理这个问题,正是出于这个原因),所以为了正确处理这个问题,你通常需要一个解析器,或者至少需要跟踪嵌套的东西。

您还有一个问题:您是否只想提取变量名称? 如果标识符表示方法,类型,类或包怎么办? 如果没有完整的解析器和完整的Java名称和类型解析,您无法解决这个问题,并且您必须在找到该语句的上下文中执行此操作。你会惊讶地发现需要多少代码才能做到这一点。

所以,如果你的目标很简单,你不关心它是否能解决这些问题,那么你可以通过一个简单的正则表达式来挑选东西 看起来像标识符。

如果你想做得好(例如,在某些生产代码中使用它),单个正则表达式将是完全灾难。你将用自己的生命向用户解释他们无法输入的内容,而这些内容永远不会有效。

总结:由于所有的复杂性,通常仅使用正则表达式处理源代码就会失败。人们不断重新学习本课程。词法生成器广泛应用于语言处理工具是其中一个主要原因。