出于某些原因,不能使用eveything这个软件,但是这个软件的功能是真挺好用的,免去了在文件海洋中寻找;
扫描模块
import os
import re
import subprocess
import time
import string
'''
获取盘符列表,返回元组,元组第一个元素为bool值,表示函数成功与否,第二个值表示原因(失败的情况,字符串形式)或结果(成功的情况,列表形式)
'''
def get_DriveLetter():
result_str = subprocess.run('''powershell $my_path = $env:temp + '\Get-Volume.txt';(Get-Volume).DriveLetter |Out-File -Encoding utf8 $my_path''')
if result_str.returncode == 0:
env_temp_path_str = os.environ['TEMP'] + r'\Get-Volume.txt'
with open(env_temp_path_str, 'r', encoding='utf-8_sig')as fd:
result = fd.read().splitlines()
return (True, result)
else:
return (False, "cmd_Fail")
def get_DriveLetter_v2():
'''
获取盘符列表,返回元组,元组第一个元素为bool值,表示函数成功与否,第二个值表示原因(失败的情况,字符串形式)或结果(成功的情况,列表形式)
此为v2版本,不同于v1版本使用powershell命令,而是通过猜测验证其是否存在。
相比V1版本,会节约一点点时间
'''
result = []
for i in string.ascii_uppercase:
if os.path.exists(i+ ':\\'):
result.append(i)
#else的情况不考虑停止,实际上,用户的磁盘盘符完全可以不连续。
return (True, result)
'''
初始目录initial_path已被记录,本次不记录
因为重复的文件名或目录名,并且要考虑到以后有拓展信息字段的需求,所以数据形状设计为这样:{'name':[['path1','other'],['path2','other'],]}
规定,初始目录字符串结尾必须带\
'''
def get_fileList(all_file_dict, initial_path):
if os.path.isdir(initial_path):
# 部分目录不允许访问,不过这部分目录也不在我的需求范围内。
try:
child_file_list = os.listdir(initial_path)
except:
print(initial_path)
return
for i in child_file_list:
if i in all_file_dict.keys():
all_file_dict[i].append([initial_path, ])
else:
all_file_dict[i] = [[initial_path, ], ]
new_path_str = initial_path + i + '\\'
if os.path.isdir(new_path_str):
get_fileList(all_file_dict, new_path_str)
def get_fileList_v2(all_file_dict, initial_path):
'''
获取文件名、路径信息
本函数是v2版本,用以提升扫描性能,用法与V1版本相同。
initial_path:必定是目录
'''
try:
child_file_list = os.scandir(initial_path)
except:
#print(initial_path)
return
for file_ in child_file_list:
if file_.name in all_file_dict.keys():
all_file_dict[file_.name].append([initial_path, ])
else:
all_file_dict[file_.name] = [[initial_path, ], ]
#是否是目录
if file_.is_dir():
get_fileList_v2(all_file_dict,file_.path)
'''
将结果字典格式化输出到文件中存储。
输出到用户家目录下存放,名称叫eveything_data.data
'''
def Formatted_output(all_file_dict):
out_file_path_str = os.environ['USERPROFILE'] + '\\eveything_data.data'
with open(out_file_path_str, 'w', encoding='utf-8')as fd:
for key, value in all_file_dict.items():
for value_i in value:
line_str = key
for value_i_i in value_i:
line_str += "\t{}".format(value_i_i)
fd.write(line_str + '\n')
'''
直接输出
输出到用户家目录下存放,名称叫eveything_data.data
'''
def Direct_output(all_file_dict):
out_file_path_str = os.environ['USERPROFILE'] + 'eveything_data.data'
with open(out_file_path_str, 'w', encoding='utf-8')as fd:
print(all_file_dict, file=fd)
'''
主控函数
'''
def main():
start_time = time.time()
result = get_DriveLetter_v2()
if result[0]:
for i in result[1]:
get_fileList_v2(all_file_dict=all_file_dict, initial_path='{}:\\'.format(i))
# get_fileList(all_file_dict = all_file_dict,initial_path ='F:\\temp\\')
Formatted_output(all_file_dict)
# Direct_output(all_file_dict)
end_time = time.time()
print("扫描花费时间:{}".format(end_time - start_time))
return all_file_dict
'''
全局变量,主要是为了共享变量,与交互式命令行中的代码
'''
all_file_dict = {}
if __name__ == '__main__':
main()
人机交互模块
import 扫描模块 as saomiao
import re
import time
import threading
import os
from copy import _reconstruct
import colorama
import traceback
def get_data_csv(filepath_str):
'''
读取csv格式的数据文件(以制表符为分隔符的文件)
'''
if os.path.exists(filepath_str):
with open(filepath_str,'r',encoding= 'utf-8-sig') as fd:
lines_list= fd.read().splitlines()
lines_list_ = [i.split('\t') for i in lines_list]
return lines_list_
else:
print("路径:{};不存在!".format(filepath_str))
return []
def get_data_dict():
'''
从扫描模块中直接获取其运行结果数据,这里会比较慢,大概在5-10分钟才能获取到结果
'''
saomiao.main()
#检验是否含有中文字符
def is_contains_chinese(strs):
for _char in strs:
if '\u4e00' <= _char <= '\u9fa5':
return True
return False
#计算字符串中中文字符个数
def count_chinese_num(strs):
ret_num = 0
for _char in strs:
if '\u4e00' <= _char <= '\u9fa5':
ret_num +=1
return ret_num
def count_notASCII_num(strs):
'''
计算字符串中的非英文字符个数;
不咋个完美,字符问题真他妈头大,有些非英文字符只占用一个字节,有些又占用两个,喵了个咪的!
'''
return int((len(strs.encode()) - len(strs))/2)
def beautify_output(data_result,regex_str):
'''
专门美化输出的函数
参数是列表类型
原本还计划着说根据命令行窗口大小来进行省略超长部分,但是我本就是查询路径信息,省略了我如何看呢,所以便不进行此操作
'''
#查询出最长的文件名长度
name_Maxlen =0
for i in data_result:
if len(i[0]) > name_Maxlen:
name_Maxlen = len(i[0])
#获取命令行的高
terminal_high = os.get_terminal_size()[1]
#输出
num_line = 0 #记录输出的行数
for i in data_result:
#目前只有两列信息,名称和路径,后续需要的话再加即可
key_words_str = re.findall(regex_str,i[0],re.I)[0]
key_words_index = i[0].find(key_words_str)
print("{}\033[1;32;40m{}\033[0m{}{} | {}".format(i[0][0 : key_words_index],i[0][key_words_index : key_words_index + len(key_words_str)],i[0][key_words_index + len(key_words_str) :] , ' ' * (name_Maxlen - len(i[0]) - count_notASCII_num(i[0])),i[1]))
num_line += 1
if num_line % (terminal_high - 2) == 0: #这里并不是很准确的显示下一页,因为某些文件的路径过长会导致换行输出。
print("按下字母q结束输出,按其他键继续输出下一页:",end='')
in_str = input()
if in_str == 'Q' or in_str == 'q':
break
print("总计{}个结果!".format(len(data_result))) #这里输出的是文件的路径数量(即文件可能有重复)
print("输出完毕!\n\n")
def beautify_output_v2(data_result,regex_str):
'''
专门美化输出的函数V2:新增自适应命令行界面输出的功能
参数是列表类型
原本还计划着说根据命令行窗口大小来进行省略超长部分,但是我本就是查询路径信息,省略了我如何看呢,所以便不进行此操作
'''
#print("data_result = {}".format(data_result))
#累计输出了多少行
exported_lines_num_int = 0
while True:
name_Maxlen =0
temp_list = []
#计算当前应该输出多少行
out_lines_num_int = int(os.get_terminal_size()[1]) - 2
if (out_lines_num_int + exported_lines_num_int) > len(data_result):
if exported_lines_num_int == 0:
temp_list = data_result[exported_lines_num_int :]
else:
temp_list = data_result[exported_lines_num_int - 1:]
else:
if exported_lines_num_int == 0 :
temp_list = data_result[exported_lines_num_int :exported_lines_num_int + out_lines_num_int]
else:
temp_list = data_result[exported_lines_num_int - 1 :exported_lines_num_int + out_lines_num_int]
for i in temp_list:
if len(i[0]) > name_Maxlen:
name_Maxlen = len(i[0])
#print('name_Maxlen={}'.format(name_Maxlen))
#print('out_lines_num_int={}'.format(out_lines_num_int))
for i in temp_list:
#目前只有两列信息,名称和路径,后续需要的话再加即可
key_words_str = re.findall(regex_str,i[0],re.I)[0]
key_words_index = i[0].find(key_words_str)
print("{}\033[1;32;40m{}\033[0m{}{} | {}".format(i[0][0 : key_words_index],i[0][key_words_index : key_words_index + len(key_words_str)],i[0][key_words_index + len(key_words_str) :] , ' ' * (name_Maxlen - len(i[0]) - count_notASCII_num(i[0])),i[1]))
exported_lines_num_int += out_lines_num_int
if exported_lines_num_int >= len(data_result):
break
print("按下字母q结束输出,按其他键继续输出下一页:",end='')
in_str = input()
if in_str == 'Q' or in_str == 'q':
break
print("总计{}个结果!".format(len(data_result))) #这里输出的是文件的路径数量(即文件可能有重复)
print("输出完毕!\n\n")
def beautify_output_v3(data_result,regex_str):
'''
专门美化输出的函数V3:新增命令支持,暂时只支持open打开文件;输出下标
参数是列表类型
原本还计划着说根据命令行窗口大小来进行省略超长部分,但是我本就是查询路径信息,省略了我如何看呢,所以便不进行此操作
'''
#print("data_result = {}".format(data_result))
#累计输出了多少行
exported_lines_num_int = 0
#总数据长度的字符长度
all_data_len_int = len(str(len(data_result)))
if len(data_result) != 0:
while True:
name_Maxlen =0
temp_list = []
#计算当前应该输出多少行
out_lines_num_int = int(os.get_terminal_size()[1]) - 2
if (out_lines_num_int + exported_lines_num_int) > len(data_result):
if exported_lines_num_int == 0:
temp_list = data_result[exported_lines_num_int :]
else:
temp_list = data_result[exported_lines_num_int - 1:]
else:
if exported_lines_num_int == 0 :
temp_list = data_result[exported_lines_num_int :exported_lines_num_int + out_lines_num_int]
else:
temp_list = data_result[exported_lines_num_int - 1 :exported_lines_num_int + out_lines_num_int]
for i in temp_list:
if len(i[0]) > name_Maxlen:
name_Maxlen = len(i[0])
#print('name_Maxlen={}'.format(name_Maxlen))
#print('out_lines_num_int={}'.format(out_lines_num_int))
for i in temp_list:
#目前只有两列信息,名称和路径,后续需要的话再加即可
key_words_str = re.findall(regex_str,i[0],re.I)[0]
key_words_index = i[0].find(key_words_str)
print("{}{} | {}\033[1;32;40m{}\033[0m{}{} | {}".format(exported_lines_num_int + 1 , ' ' * (all_data_len_int - len(str(exported_lines_num_int + 1))), i[0][0 : key_words_index],i[0][key_words_index : key_words_index + len(key_words_str)],i[0][key_words_index + len(key_words_str) :] , ' ' * (name_Maxlen - len(i[0]) - count_notASCII_num(i[0])),i[1]))
exported_lines_num_int += 1
#print("按下字母q结束输出,输入:open 序号 可以打开对应的文件;按其他键继续输出下一页:",end='')
processed_cmd_ret_int = dealWith_user_cmd(data_result)
if processed_cmd_ret_int == -1:
break
elif processed_cmd_ret_int == 0:
#什么都不做
pass
if exported_lines_num_int >= len(data_result):
break
print("总计{}个结果!输出完毕!\n\n".format(len(data_result)))
def dealWith_user_cmd(data):
'''
处理用户输入的命令
返回0表示继续输出
返回-1表示结束输出
'''
while True:
print("按下字母q结束输出;输入::open 序号 可以打开对应的文件;输入::del 序号 可以删除对应的文件;按其他键继续输出下一页:", end='')
cmd_str = input()
if len(cmd_str) == 0:
return 0
if cmd_str == 'Q' or cmd_str == 'q':
return -1
elif cmd_str[0] == ':':
#open命令最少7个字符
if len(cmd_str) >= 7:
if cmd_str[1:5] == 'open':
num_ = cmd_str[5:]
index_int = re.findall(r'\d+',num_)
if len(index_int) > 0:
index_int = int(index_int[0])
if index_int <= len(data):
ret_ = open_file(data[int(index_int) - 1][1] + "\\" + data[int(index_int) - 1][0])
if(ret_):
print("文件(夹)打开成功。")
else:
print("文件(夹)打开失败。")
else:
print("不存在这条数据!")
#return 1
elif len(cmd_str) >= 6:
#del命令最少5个字符
if cmd_str[1:4] == 'del':
num_ = cmd_str[4:]
index_int = re.findall(r'\d+',num_)
if len(index_int) > 0:
index_int = int(index_int[0])
if index_int <= len(data):
ret_ = del_file(data[index_int - 1])
if(ret_):
print("文件(夹)删除成功。")
else:
print("文件(夹)删除失败。")
else:
print("不存在这条数据!")
#return 1
else:
return 0
else:
return 0
def Human_computer_interaction():
'''
处理用户输入和系统的提示性输出的,返回元组,一个是原始输入的,另一个是处理之后的
'''
while True:
print('请输入需要查找的文件表达式:')
regex_str = input()
if regex_str == '':
continue
else:
#需要判断输入的正则表达式是否合法,不合法的话,就需要提示用户重新输入,之所以不纠正是因为不要猜测用户的意图。
try:
re.compile(regex_str)
except:
print("输入的正则表达式有误!")
continue
if regex_str[0] == '^':
regex_str = regex_str[1:]
replace_result_str = r"(?<=\t)"
else:
replace_result_str = r'(?<=\t)[^\t]*?'
for index in range(len(regex_str)):
if regex_str[index] == '.':
if index != 0 and regex_str[index - 1] == '\\':
replace_result_str += regex_str[index]
else:
replace_result_str += r'[^\t]'
else:
replace_result_str += regex_str[index]
if replace_result_str[-1] == "$":
replace_result_str = replace_result_str[0:-1]
replace_result_str += r'(?=\t)'
else:
replace_result_str += r'[^\t]*?(?=\t)'
#print(replace_result_str)
return (regex_str,replace_result_str)
def open_file(filepath):
'''
打开指定的文件,使用系统默认方式;
打开之前会检查文件/目录是否存在。
成功返回True,不成功返回False
'''
if os.path.exists(filepath):
try:
os.startfile(filepath)
except OSError as msg:
traceback.print_exc()
return False
return True
else:
print("该文件已经不存在了。")
return False
def del_file(line_list):
'''
删除该文件,需要考虑
是否有权限
删除之后需要同步删除字典里面的数据中的路径,在这中,实质上是删除路径,若是路径只有一个,则键也需要删除
如何杀死当前子进程,新起一个子进程用于更新数据。 未完成
完成删除返回True,否则返回False
'''
name = line_list[0];
path = line_list[1];
file_path = "{}\\{}".format(path,name);
ret_num = del_dictElement(name,path);
if(ret_num == 1):
if(del_file_system(file_path)):
return True
else:
return False
else:
return False
def del_file_system(filepath):
'''
实际去做删除文件、目录操作的函数
成功删除(或者不存在)返回True;否则返回FALSE
'''
if(os.path.exists(filepath)):
try:
if(os.path.isdir(filepath)):
os.rmdir(filepath)
else:
os.remove(filepath)
except e:
print(e)
return False
return True
else:
#文件不存在了,虽非我之功,但也完成目的了。
return True
def del_dictElement(name,path):
"""
操作字典数据变量,删除对应的path,必要时还需删除对应key
需要返回值,返回值含义
0:表示扫描字典数据为空,未完成第一次扫描,此时不能执行扫描操作
1:表示完成删除操作
-1:未找到对应路径,未能完成删除
-2:没有找到key,即文件名不存在与字典数据中
"""
global saomiao_result_data
if(len(saomiao_result_data)>0):
if(name in saomiao_result_data.keys()):
if(len(saomiao_result_data[name])>1):
#不需要删除key
list_ = saomiao_result_data[name]
index = 0
is_del = False
for path_i in list_:
if(path_i == path):
del saomiao_result_data[name][index]
is_del = True
index += 1
if(not is_del):
print("字典数据删除失败!未找到该路径")
return -1
else:
return 1
else:
#需要删除key
del saomiao_result_data[name]
return 1
else:
print("找不到key!")
return -2
else:
print("此时还不能执行删除操作哦,请耐心等待一下。")
return 0
def restart_childThread():
'''
重启子线程
无返回值,默认全成功
如何杀死子进程是个麻烦事儿
'''
global threading_Operating_condition
if(threading_Operating_condition != ""):
#存在子进程且子进程正在运行才有意义
if(threading_Operating_condition.is_alive()):
#子进程还在运行,重启
pass
else:
#子进程已结束,重运行
threading_Operating_condition = threading.Thread(target = get_data_dict)
threading_Operating_condition.start()
else:
#不需要做处理
pass
def find_match(data,regex_str):
'''
查询函数,从列表中读取
'''
#print("regex_str = {}".format(regex_str))
start_time = time.time()
result_list = []
for i in data:
result_regex = re.findall(regex_str,i[0],re.I)
if len(result_regex) != 0:
result_list.append(i)
print('查询花费:{}秒。'.format(time.time() - start_time))
return result_list
def find_match_dict(data,regex_str):
'''
查询函数,从字典中读取,并生成结果列表类型变量
'''
start_time = time.time()
result_list = []
if type({}) == type(data):
for key in data.keys():
result_regex = re.findall(regex_str,key,re.I)
if len(result_regex) != 0:
temp_ = {}
temp_[key] = data[key]
result_list += dict_Conversion_list(temp_)
else:
print("未传入字典类型变量!")
exit()
print('查询花费:{}秒。'.format(time.time() - start_time))
return result_list
def find_match_dict_v2(data,regex_str):
'''
查询函数V2,从字典中读取,并生成结果列表类型变量,先将可以聚合成大字符串,在一次性匹配出所有符合条件的值。
'''
start_time = time.time()
result_list = []
if type({}) == type(data):
keys = data.keys()
keys_str = "\t"
keys_str += '\t'.join(keys)
keys_str += '\t'
result_key = re.findall(regex_str,keys_str,re.I)
print("匹配出{}个结果。".format(len(result_key))) #这里输出的是不重复的文件数量
#print("result_key = {}".format(result_key))
for key in result_key:
temp_ = {}
temp_[key] = data[key]
result_list += dict_Conversion_list(temp_)
else:
print("未传入字典类型变量!")
exit()
print('查询花费:{}秒。'.format(time.time() - start_time))
#print("result_list = {}".format(result_list))
return result_list
def dict_Conversion_list(dict_data):
'''
将字典键和值转变为列表
'''
if type({}) != type(dict_data):
return []
return_result = []
for key,value in dict_data.items():
for value_i in value:
temp_list = []
for value_i_i in value_i:
temp_list.append(key)
temp_list.append(value_i_i)
return_result.append(temp_list)
return return_result
def my_deepcopy(x):
'''
深拷贝,copy库的deepcopy性能不太行,所以在deepcopy的基础上改了一下
这个深拷贝函数仅用于本脚本中,copy库中的deepcopy函数慢是有原因的,了解之后再使用。
'''
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
y = _reconstruct(x, None, *rv)
return y
def main():
colorama.init(autoreset=True) #启用颜色模块
#程序刚运行时使用之前扫描的格式化输出结果文件
data_file_path_str = r'C:\Users\gyj\eveything_data.data'
data = get_data_csv(data_file_path_str)
global threading_Operating_condition
global saomiao_result_data
while True:
if threading_Operating_condition != '' and threading_Operating_condition.is_alive() == False:
#线程结束,可以获取数据了
_time_ = time.time()
#释放空间
if saomiao_result_data != '':
saomiao_result_data = ""
saomiao_result_data = my_deepcopy(saomiao.all_file_dict)
#print("深拷贝所用时间:{}".format(time.time() - _time_))
#深拷贝完成之后清除全局变量的数据,避免出现重复值的bug,同时节约空间
saomiao.all_file_dict.clear()
#销毁从文件中读取的数据
if 'data' in locals().keys():
del data
#print("数据量:{}".format(len(saomiao_result_data)))
#只有在线程结束或者未开始时才运行
if threading_Operating_condition == '' or threading_Operating_condition.is_alive() == False:
#get_data_dict()
threading_Operating_condition = threading.Thread(target = get_data_dict)
threading_Operating_condition.start()
user_in_tuple = Human_computer_interaction()
result_list = []
if saomiao_result_data != '':
result_list = find_match_dict_v2(data = saomiao_result_data,regex_str = user_in_tuple[1])
else:
#列表查询用原始输入
result_list = find_match(data = data,regex_str = user_in_tuple[0])
beautify_output_v3(result_list,user_in_tuple[0])
saomiao_result_data = '' #全局变量,存储扫描模块返回来的数据
threading_Operating_condition = '' #子线程运行情况
if __name__ == '__main__':
main()
我很尽力的去做对齐了,但是这个超出了我能力范围,字符真是个好垃圾!
历经几次迭代和优化,现在查询96万个结果只需要3秒左右,优化显示效果,优化正则表达式匹配的速度
更新-----现在扫描的速度很快了,当然是不能跟everything相比了,它是直接读取ntfs的文件表而非扫描的。
运行结果: