识别并提取Python程序中的类名、函数名、变量名等标识符。假设源文件的编写风格符合Python语言编程规范。
假设程序文件为FindIdentifiersFromPyFile.py,在命令提示符环境中使用命令“Python FindIdentifiersFromPyFile.py
目标文件名”查找并输出目标文件中的标识符。
import re # 导入re模块
import os # 导入os模块
import sys # 导入sys模块
classes = {} # 定义一个类,其类名为空
functions = [] # 定义一个列表,列表里为空
variables = {'normal': {}, 'parameter': {}, 'infor': {}} # 定义一个字典其中键的值为normal、parameter、infor为形参{}存放相应的值
'''This is a test string:
atest,btest=3,5
to verify that variables in comments will be ignored by this algorithm
''' # 用来提示信息,测试字符序列用来验证注释中的变量将被此算法忽略,来验证注释的准确性
def _identifyClassNames(idex, line): # 定义一个函数,此函数有两个形参
''':parameter index is the line number of line,
:parameter line is a line of code of the file to check
''' # 形参parameter、index是line的行号,参数索引用于检查文件
pattern = re.compile(
r'(?<=class\s)\w+(/=.*?:)$') # 定义一个对象并用正则表达式,其中这个正则表达式表示如果“=”后面的字符出现则匹配,class子模板以ls
# 匹配任何字符以数字,字符或下划线开头,以若是"="前面出现的字段则匹配反之亦然为字段结尾
matchResult = pattern.serch(line) # 定义一个变量并用pattern访问search方法来进行初始化
if not matchResult: # 调用if语句进行判断mathResul的值是否正确
return # 返回语句
className = matchResult.group(0) # 定义一个变量并进行初始化,对象方法来进行
classes[className] = classes.get(className, []) # 定义一定空间的列表用对象get方法来对其初始化
classes[className].append(index) # 调用列表名方法来用append()进行追加
def _indetifyFunctionNames(index, line): # 定义一个函数该函数有形参
pattern = re.compile(
r'(?<=def\s)(\w+)\((.*$?)\)(?=:)$') #
# 定义一个变量并调用正则表达式对其初始化,该正则表达式的含义是以如果“=”后面的字符出现则匹配反之或以匹配数字、字母、下划线只匹配一个字符为行首以除换行符出现0次或多次匹配一个字符式后面的字符出现则匹配为行尾
matchResult = pattern.search(line) # 定义一个变量,并用pattern访问,search方法来进行初始化
if not matchResult: # 调用if语句进行判断,matchResult的值是否正确
return # 返回语句
functionName = matchResult.group(1) # 定义一个变量并进行初始化,通过对象方法来对其初始化
functions.append((functionName, index)) # 调用append方法由于函数的形参有两个须与方法参数保持一致
parameters = matchResult.group(2).split(r',') # 调用对象的第二个元素的split方法以,分隔
if parameters[0] == '': # 调用if语句,判断parameters的第一个列表元素是否为空
return # 返回语句
for v in parameters: # for循环
variables['parameter'][v] = variables['parameter'].get(v, []) # 调用variables第一个参数来访问get()方法对variables形参的列表初始化
variables['parameter'][v].append(index) # 调用对象的append进行元素追加
def _identifyVariableNames(index, line): # 定义一个函数,该函数有两个参数
# find normal variables ,including the case:a,b =3,5
pattern = re.compile(r'\b(.*?)(?=\s=)$') # 定义一个变量并用正则函数表达式表示以单词类或单词尾r开头出现0或1次为行首,以空白字符为行尾
matchResult = pattern.search(line) # 定义一个变量,并用pattern,search()对其初始化
if matchResult: # 调用if语句,进行判断
vs = matchResult.group(1).split(r', ') # 定义一个变量vs,并用对象split()以","对其进行分隔
for v in vs: # 进入for循环
# consider the case 'if variable == value'
if 'if' in v: # if判断语句,是否在v集合中
v = v.split()[1] # 调用split()方法,对变量v进行初始化
# consider the case :'a[3]=3'
if '[' in v: # 判断'['是否在v中
v = v[0:v.index('[')] # 调用index()方法,访问第一个元素并存取
variables['normal'][v] = variables['normal'].get(v, []) # 调用对象get()来对variables['normal'][v]初始化
variables['normal'][v].append(index) # 调用append()方法进行追加
# find the variables in for statements
pattern = re.compile(
r'(?<=for\s)(,*?)(?=\sin)$') # 定义一个变量并用正则表达式,其中这个正则表达式表示以r开头或结尾的,对比后面的字符出现则匹配为行首,以出现0或1次的空字符或看是否出现0或多次sin为行首
matchResult = pattern.search(line) # 调用search()进行初始化
if matchResult: # 判断matchResult是否合理
vs = matchResult.group(1).split(r', ') # 对marchResult的第一个元素进行处理,以","作为分隔
for v in vs: # for循环
variables['infor'][v] = variables['infor'].get(v, []) # 调用get()对其初始化
variables['infor'][v].append(index) # 调用append()方法进行追加
def output(): # 定义一个方法
print('=' * 30) # 调用print()其中'='表示如果后面的字符出现则匹配,匹配30位任意字符,反之为空
print('The class names and their line numbers are:') # 输出提示信息
for key, value in classes.items(): # for循环
print(key, ':', value) # 输出键和值
print('=' * 30) # 调用print()其中'='表示如果后面的字符出现则匹配,匹配30位任意字符,反之为空
print('The function names and their line numbers are:') # 输出提示信息
for i in functions: # for循环
print(i[0], ':', i[1]) # 输出其字典的键和值
print('=' * 30) # 调用print()其中'='表示如果后面的字符出现则匹配,匹配30位任意字符,反之为空
print('The normal variable names and their line numbers are:') # 输出提示信息
for key, value in variables['parameter'].items(): # for循环
print(key, ':', value) # 输出键和值
print('=' * 20) # 调用print()其中'='表示如果后面的字符出现则匹配,匹配20位任意字符,反之为空
print('The parameter names and their line numbers in functions are:') # 输出提示信息
for key, value in variables['infor'].items(): # for循环
print(key, ':', value) # 输出键和值
# suppose the lines of comments less than 50
def comments(index): # 定义一个函数,index为形参
for i in range(50): # for循环
line = allLines[index + i].strip() # 调用allLines[]求得行数
if line.endswith('"""') or line.endswith("'''"): # 调用if语句判断
return i + 1 # 返回i值+1
if __name__ == '__main__': # 脚本独立运行,则其__name__属性值被自动设置为'__main__'
fileName = sys.argv[1] # 定义一个变量,并调用argv[]对其初始化
if not os.path.isfile(fileName): # 判断操作系统的路径名是不是fileName
print('Your input is not a file.') # 输出提示信息
sys.exit(0) # 退出if语句
if not fileName.endswith(('.py', '.pyw')): # 判断fileName以.py或.pyw结尾的文件
print('Sorry.I can only check Python source file.') # 输出提示信息
sys.exit(0) # 退出if语句
allLines = [] # 此时allLines为空的列表
with open(fileName, 'r') as fp: # 如果读取不存在以'r'的文件,则会出现error错误提示
allLines = fp.readlines() # 调用readlines()方法来读取fp行数返回给它
index = 0 # 此时索引为0
totalLen = len(allLines) # 调用len()求出allLines的长度
while index < totalLen: # 调用while语句判断index位置合理
line = allLines[index] # 定义一个变量求出allLines当前位置
# strip the blank characters at both end of line
line = line.strip() # 进行line打包处理
# ignore the comments starting with '#'
if line.startswith('#'): # if语句判断line是否以#开头
index += 1 # index=index+1
continue # 退出当前循环
# ignore the comments between ''' or """
if line.startswith('"""') or line.startswith("'''"): # if语句判断
index += comments(index) # index=index.comments(index)
continue # 退出当前循环
# identify identifiers
_identifyClassNames(index + 1, line) # 调用函数实现语句
_indetifyFunctionNames(index + 1, line) # 调用FunctionNames()函数实现下标的索引转换
_identifyVariableNames(index + 1, line) # 调用VariableNames()函数实现下标的增减转换
index += 1 # index=index+1
output() # 退出爬虫