Java 使用正则表达式从代码语句中提取变量

Java 使用正则表达式从代码语句中提取变量,java,regex,pattern-matching,Java,Regex,Pattern Matching,我试图从代码语句和“if”条件中提取变量。我有一个正则表达式,但是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_integ

我试图从代码语句和“if”条件中提取变量。我有一个正则表达式,但是
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);
        }
    }
}

您需要删除分别在字符串开头和结尾声明位置的
^
$
锚,并使用
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);
}
看,结果是:

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

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

如果您只想挑选标识符(我们将在下面进一步讨论变量),那么您就有机会使用正则表达式(毕竟,这就是lexer的构建方式)

但是你可能需要一个比你现在拥有的更复杂的版本,即使有其他作者建议的修改

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

 [a-zA-Z_$][a-zA-Z_$0-9]* - (if | else | class | ... )
我们的DMS词法生成器[见我的简历]有减法正则表达式,因为这在语言词法分析中非常有用)

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

接下来,Java中的标识符允许使用各种Unicode字符(拉丁语、俄语、汉语等)。regexp可以识别这些字符,它比您提出的简单的“a-Z”样式大得多

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

a =  "x=y+z/n-10+my5th_integer+201";
\u0061 = \u0062; //  means  "a=b;"
a  = $"x+{y*$"z=${c /* p=q */}"[2]}*q" + b;
这里只有一个标识符。注释也会出现类似的问题 包含类似于语句的内容:

/* Tricky:
   a =  "x=y+z/n-10+my5th_integer+201";
*/
对于Java,您也需要担心Unicode转义。考虑这个有效的java语句:

a =  "x=y+z/n-10+my5th_integer+201";
\u0061 = \u0062; //  means  "a=b;"
a  = $"x+{y*$"z=${c /* p=q */}"[2]}*q" + b;
或更恶劣:

a\u006bc = 1; //  means "akc=1;" not "abc=1;"!
如果没有Unicode字符解码,您甚至可能 注意一个字符串。以下是上述的一个变体:

a =  \u0042x=y+z/n-10+my5th_integer+201";
要正确提取标识符,您需要构建(或使用)完整Java lexer的等价物,而不仅仅是简单的正则表达式匹配

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

您很幸运,因为您正在尝试为Java做一些事情。如果必须为C#语言(一种非常类似的语言)执行此操作,则必须处理插值字符串,这允许字符串中包含表达式。表达式本身可以包含字符串。。。它的乌龟一路下来。考虑C(6)语句:

a =  "x=y+z/n-10+my5th_integer+201";
\u0061 = \u0062; //  means  "a=b;"
a  = $"x+{y*$"z=${c /* p=q */}"[2]}*q" + b;
它包含标识符a、b、c和y。其他每一个“标识符”实际上只是一个字符串或注释字符。PHP具有类似的插值字符串

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

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

所以,如果你的目标很简单,你不在乎它是否能解决这些复杂问题,你可以用一个简单的正则表达式来挑选一些东西 看起来像是标识符

如果您想很好地使用它(例如,在某些生产代码中使用它),那么单个正则表达式将是一场灾难。你将用一生的时间向用户解释他们无法键入的内容,而这永远不会起作用


摘要:由于所有的复杂性,通常只使用正则表达式处理源代码都会失败。人们不断地重新吸取教训。词法生成器广泛应用于语言处理工具中,这是一个关键原因。

字符串中的第二个字符是
=
,但您的正则表达式不允许任何
=
。你的正则表达式也没有任何组。我不知道“=”将如何影响正则表达式,因为我对正则表达式是全新的。我使用了.start()和.end(),但也不起作用,在这个例子中,我希望x、y、z、n和my5th_整数是结果,因为它们是变量。这是一个很好的解释,您肯定是对的,但出于我的目的,我只需要上面的正则表达式,因为我正在对伪代码进行数据流分析,我只需要提取变量名称,我唯一关心的关键字是:if、else、elseif、true和false,这样我就可以轻松地处理它们了。你说的是Java:}如果你想进行数据流分析(不清楚这在psuedocode中有什么用处),你仍然需要解析和进行名称解析。请参阅我关于“解析后的生活”的文章(谷歌或via bio)。从根本上说,我们回到了“你打算制造一个玩具(用于教育目的)”,这意味着只要你明白你在作弊,任何作弊都是可以的,或者