python解析Java文件三方库javalang用法简介
创始人
2025-05-30 04:34:24
0

书接上文,在我一顿操作猛如虎的土鳖扫描后(python提取android工程代码中的一些数据),发现仅仅只是扫描出关键字的话,有些封装后的调用或者将关键字声明称常量后的调用都没法识别出来。这种关键字扫描怎么说呢?有点鸡肋,误报太多,无法聚焦重点。所以我们需要一个识别关键字是否为变量或者封装在那个函数中的能力。这个相当于解析整个Java文件,按照基础的Java文件结构进行归类查询。这种功能其实就是IDE的实现跳转功能,想着这么成熟的技术一定有库的,又去搜索了一波,总算发现了这个宝藏库——javalang。

javalang是一个用于处理Java源代码的纯Python库。javalang提供了针对Java 8的词法分析器和解析器。该实现基于http://docs.oracle.com/javase/specs/jls/se8/html/上提供的Java语言规范。

javalang官方首页上如是说。

安装方法

pip install javalang

使用方法简介

  1. 将待解析文件读入内存后拼接成一起字符串传入方法tree = javalang.parse.parse(full_content)中。如此我们便将这个Java文件解析完成了。
  2. 遍历这个解析结果,筛选我们想要的东西。比如包名、依赖列表、类名、变量以及方法等。调用方法如下:
# 包名
print("package:")
print(tree.package.name)
print("######################################")# 依赖
print("imports:")
for i in tree.imports:print(str(i.path))
print("######################################")# 方法名
print("方法名:" + tree.types[0].methods[1].name)
print("方法修饰符:" + str(tree.types[0].methods[1].modifiers))
print("第一入参名:" + str(tree.types[0].methods[1].parameters[0].name))
print("第一入参类型:" + str(tree.types[0].methods[1].parameters[0].type.name))
print("返回值类型:" + str(tree.types[0].methods[1].return_type.name))
print("######################################")

3.javalang这个库解析的十分到位,将其中声明对象的类名以及子类名等都有分门别类的分割,甚至还把每行命令代码都进行了解析。穷尽显然是没必要的,想用什么自己在他的tree.py文件中可以找到声明方式,然后就可以一步步的打出来了。相信聪明的各位读者,也懒得看我在这儿千篇一律的举例子。以上面漏掉的成员类型为例,抛砖引玉。

从上面的例子中我们能知道tree下一级中types是类列表,取第一项打印一下可以得到。

ClassDeclaration(annotations=[], body=[FieldDeclaration(annotations=[], declarators=[VariableDeclarator(dimensions=[], initializer=Literal(postfix_operators=[], prefix_operators=[], qualifier=None, selectors=[], value=“android.permission.RECEIVE_SMS”), name=RECEIVE_SMS)], documentation=None, modifiers={‘static’, ‘public’, ‘final’}, type=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None)), MethodDeclaration(annotations=[], body=[ReturnStatement(expression=Literal(postfix_operators=[], prefix_operators=[], qualifier=None, selectors=[], value=“hello world”), label=None)], documentation=None, modifiers={‘public’}, name=testReturn, parameters=[FormalParameter(annotations=[], modifiers=set(), name=testInt, type=BasicType(dimensions=[], name=int), varargs=False), FormalParameter(annotations=[], modifiers=set(), name=testString, type=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None), varargs=False)], return_type=ReferenceType(arguments=None, dimensions=[], name=String, sub_type=None), throws=None, type_parameters=None)], documentation=/** * desc : 权限请求实体类 */, extends=None, implements=None, modifiers={‘public’, ‘final’}, name=Permission, type_parameters=None)

分析其中,发现变量在FieldDeclaration中,找到tree.py文件中这个的声明可以看到:

class TypeDeclaration(Declaration, Documented):attrs = ("name", "body")@propertydef fields(self):return [decl for decl in self.body if isinstance(decl, FieldDeclaration)]

于是可以知道通过tree.types[i].fields可以获得变量集合。根据此可以写出如下的遍历逻辑:

# 成员变量
print("成员变量:")
for f in tree.types[0].fields:declare = ''# 声明变量修饰符for m in f.modifiers:declare += m + " "# 声明变量类型declare += str(f.type.name) + " "# 声明变量名declare += f.declarators[0].nameprint(declare)
print("######################################")

其他的Java解析方法都可以根据上述流程逐步整理出来,然后想怎么用就看你的心情咯。

解析一个Java文件

基于上面的介绍,我将一个Java文件解析后重新打印了出来,具体逻辑如下。其余还有关于Interface,Enum等解析等后面用到再来解析吧。其中file_reader.read_file_and_format详见python提取android工程代码中的一些数据。

import javalang.parsefrom permission_checker import file_readerdata = file_reader.read_file_and_format("test_res/Test.java")
full_content = ''
for d in data:# 需要剔除 // 开头的日志if not d.strip().startswith("//"):full_content += dtree = javalang.parse.parse(full_content)# 包名
print("package:")
print(tree.package.name)
print("######################################")# 依赖
print("imports:")
for i in tree.imports:print(str(i.path))
print("######################################")# types以class为分割一个class一个type
# 成员变量
print("成员变量:")
for f in tree.types[0].fields:declare = ''# 声明变量修饰符for m in f.modifiers:declare += m + " "# 声明变量类型declare += str(f.type.name) + " "# 声明变量名declare += f.declarators[0].nameprint(declare)
print("######################################")# 还原拼接方法名
for m in tree.types[0].methods:declare = ''# 拼接变量修饰符for mod in m.modifiers:declare += mod + " "# 拼接返回类型,当void时,没有return_type需要注意if m.return_type:declare += str(m.return_type.name)else:declare += "void"declare += " "# 拼接方法名declare += str(m.name)# 拼接入参declare += "("hasParameters = Falsefor p in m.parameters:hasParameters = Truedeclare += p.type.name + " " + p.name + ","# 去除最后一个逗号if hasParameters:declare = declare[:-1]declare += ")"print(declare)

踩到的坑

在将Java文件导入到内存中时需要将\\开头的注释过滤掉,这种注释会将后面拼接的代码一并注释掉,造成文件结构异常,导致解析失败抛出异常javalang.parser.JavaSyntaxError。但值得注意的是Javadoc类型以及\**\不会异常。

相关内容

热门资讯

jspweb综合分类信息发布系... 网站前台功能详细介绍: 注册会员:通过该模块完成用户注册操作࿰...
学生课外活动方案 学生课外活动方案(通用18篇)  为了确保事情或工作能无误进行,通常会被要求事先制定方案,方案是有很...
幼儿园六一儿童节的活动方案 幼儿园六一儿童节的活动方案(通用20篇)  为了确保事情或工作得以顺利进行,就常常需要事先准备方案,...
五一工会团建活动方案 五一工会团建活动方案(精选8篇)  为了确定活动的圆满进行,往往需要预先制定好活动方案,活动方案是为...
庆五一活动方案 庆五一活动方案(通用19篇)  为了确保活动能有条不紊地开展,时常需要预先开展活动方案准备工作,活动...
数据平民化之路(三)— 低代码... 随着多云战略的崛起,多云策略在企业中也越来越受欢迎。IBM公司最近发布的一份调查报告表...
vue中的生命周期 前言 很多时候我们希望能在 vue 生命周期的过程中执行一些操作,生命周期钩子函数也...
年度销售计划书 年度销售计划书  年度销售计划书怎么写?以下为大家分享的是年度销售计划书格式,希望对大家有所帮助。如...
幼儿园主题活动方案 幼儿园主题活动方案  为保障事情或工作顺利开展,常常需要提前进行细致的方案准备工作,方案指的是为某一...
5.15国际家庭日活动方案 5.15国际家庭日活动方案  为了确保事情或工作能无误进行,常常需要提前制定一份优秀的方案,方案是计...
开展法制宣传活动方案 开展法制宣传活动方案  为有力保证活动开展的质量水平,就常常需要事先准备活动方案,活动方案是阐明活动...
数据分享 | 全球7.77亿建... 022年5月17日,微软必应地图团队发布包含涵了776712641个建筑物轮廓数据的全球建筑物轮廓数...
老胡的周刊(第083期) 老胡的信息周刊,记录这周我看到的有价值的信息,主要针对计算机领域...
k8s etcd 文章目录一、etcd1.重新搭建集群环境2.备份3.恢复 一、etcd 官方网址:h...
外国语学校6.1儿童节活动方...   每年的6月1日是国际儿童节,这一天为了表示对孩子们的祝贺,小编特地准备了以下外国语学校6.1儿童...
手机营销活动方案 手机营销活动方案(10篇)  为保障事情或工作顺利开展,常常要根据具体情况预先制定方案,方案指的是为...
阳光冬季长跑活动方案 阳光冬季长跑活动方案范文(精选7篇)  为了确保活动有序有力开展,常常需要预先准备活动方案,活动方案...
学雷锋活动月主题活动方案 学雷锋活动月主题活动方案(精选22篇)  为了确保活动能成功举办,通常会被要求事先制定活动方案,活动...
自然语言处理基础任务(FMMB... 中文分词背景词语的概念:词语(word)是最小独立使用的音义结合体&#x...
第5章 设计模式 5.1 设计模式介绍? 5.1.1 设计模式是什么?   设计模式是指在...