在 python 中提取 java 主要 class 名称

Extracting java main class name in python

我在 python 脚本中有字符串,其中包含一些 java 代码。
我如何从中提取基础 java class 名称以便使用 subprocess?
执行它 我认为它可以使用正则表达式来实现,但我不知道如何实现。

样本:

a = """
import java.util.Scanner;
class sample{}
class second
{
    static boolean check_prime(int a)
    {
        int c=0;
        for (int i=1;i<=a; i++) {
            if(a%i==0)
                c++;
        }
        if(c == 2)
            return true;
        else
            return false;
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("Enter two numbers");
        int a = in.nextInt();
        int b = in.nextInt();
        if(check_prime(a) && check_prime(b))
        {
            if(b-a==2 || a-b==2)
                System.out.println("They are twin primes");
            else
                System.out.println("They are not twin primes");
        }
        else
            System.out.println("They might not be prime numbers");
    }
}
"""

这是一个粗略的方法:

import re

b = a.split()
str = b[b.index("class")+1]
javaclass = re.sub("{.*$","",str)
print (javaclass)

...本质上是获取所有单词,并找到第一次出现 "class" 之后的第一个单词。如果您遇到类似

的情况,它还会删除“{”及其后的任何内容
class MyClass{

但是,如果一个文件中有多个 类,则需要做更多的工作。

A main class is a class which contains the public static void main function.

如果在您的环境中可行;您可以使用可以解析 Java 源代码的库,例如 plyj or javalang:

#!/usr/bin/env python
import javalang # $ pip install javalang

tree = javalang.parse.parse(java_source)
name = next(klass.name for klass in tree.types
            if isinstance(klass, javalang.tree.ClassDeclaration)
            for m in klass.methods
            if m.name == 'main' and m.modifiers.issuperset({'public', 'static'}))
# -> 'second'

如果在 Java 源的顶部有一个包声明,例如 package your_package; 即,如果完整的 class 名称是 your_package.second 那么你可以得到包名称为 tree.package.name.

或者您可以使用解析器生成器,例如 grako 并指定一个 Java 语法子集,它足以在您的案例中获得 class 名称。如果输入是高度规则的;如果您对代码结构的假设是错误的,您可以尝试使用正则表达式并预计它会失败。

正如我在评论中所说,使用 re.findall() 像这样:

re.findall('class (\w*)', a)

作为函数名,findall()可以找到所有的class个名字。并在此处使用 \w 将匹配所有 ascii 字母(如果您使用 class MyClass{,将比 .* 更好)。


关于找到主要 class,使用 re.S 像这样:

for i in re.split('\nclass ', a)[1:]:                      # will match the main code block and the class name of all classes
    if re.search('\n\s*public static void main', i):              # check if 'public static void main' in a class
        print(re.search('(\w*)', i).group(1))       # and print out the class name

一种更简单的方法,只有一行使用列表理解:

[re.search('(\w*)', i).group(1) for i in re.split('\nclass ', a) if re.search('\n\s*public static void main', i)]

正如您猜想的那样,使用正则表达式可以近似解决问题。但是,请记住一些技巧:

  1. class 名称不能以空格结尾,因为 MyClass{ 是合法且常见的
  2. 可以在class名称后提供类型参数,例如MyClass<T>,编译后的.class文件名不受此类型参数影响
  3. 一个文件可以有多个顶层 class,但是不能声明一个 public 而这个 class 不能 与文件同名
  4. 与文件同名的publicclass可能有内部class(甚至可能是public)但是这些必须在外部 class 声明。

这些提示导致搜索短语 public class 的第一次出现,捕获下一个 运行 个字符,然后寻找空格、{< 字符.

这是我想出来的(可能有点难看):public\s*(?:abstract?)?\s*(?:static?)?\s*(?:final?)?\s*(?:strictfp?)?\s*class\s*(\w.*)\s*,?<.*$

仅使用正则表达式几乎是行不通的。作为为什么它不能的一个基本例子,考虑这个:

public class A {
     public static void ImDoingThisToMessYouUp () {
          String s = "public static void main (String[] args) {}";
     }
}

public class B {
      public static void main (String[] args) {}
}

你明白了......Regex 总是会被愚弄,以为他们找到了一些你正在寻找的东西。您必须依赖更高级的库进行解析。

我会选择 J.F。塞巴斯蒂安的回答。