用Python实现一个文本定位器

371 阅读3分钟

项目环境
使用os、threading内置模块,用于操作目录和文件,多线程相关。

click模块之前介绍过,主要用于封装命令行接口,使用方便。

Python版本是3.7.2

import os
import threading
import click

功能演示
打开命令行窗口,执行下面一行代码,使用-t指定要查找的关键字线程,-p指定查找目录,指在当前目录的父级目录下查找:

python str_finder.py -t 线程 -p ..
打印结果,分割线上是查找的文件,在此没有贴上来。

分割线下面是查找的结果,报告使用多少个线程,同时存活线程数,待查找的关键字线程位于哪些文件中,如下所示:

-------------------------------------------------------------
共使用6个线程
同时存活线程数2
线程 found in ../python-small-examples2/python-small-examples/README.md
线程 found in ../python-small-examples2/python-small-examples/md/120.md
线程 found in ../python-small-examples2/python-small-examples/md/121.md
线程 found in ../python-small-examples2/python-small-examples/md/118.md
线程 found in ../python-small-examples2/python-small-examples/md/119.md
线程 found in ../python-small-examples2/python-small-examples/md/122.md
线程 found in ../python-small-examples2/python-small-examples/md/123.md
线程 found in ../python-small-examples2/python-small-examples/all.md
线程 found in ../python-small-projects/Python200小例子.md
线程 found in ../python-small-projects/str_finder.py
线程 found in ../10个可视化作品-作者zhenguo/作品15-Matplotlib动图/爬虫案例.md

再查找雪花,使用下行命令:

python str_finder.py -t 雪花 -p ..

查找结果:

----------------------------------------------------------------------------------------------------
共使用6个线程
同时存活线程数2
雪花 found in ../python-small-examples2/python-small-examples/README.md
雪花 found in ../python-small-examples2/python-small-examples/md/141.md
雪花 found in ../python-small-examples2/python-small-examples/all.md
雪花 found in ../publish-book/视频文字稿.md
雪花 found in ../python-small-projects/Python200小例子.md

代码逐行解释
项目只由一个模块str_finder.py,模块中包括TextFinder类,下面主要讲解类的组成。

TextFinder类
TextFinder类包括:

三个类属性

对外公开方法get_files

递归私有方法__get_files

线程方法__task

下面分别介绍这四个构建。

类属性
前两个类属性已注释,第三个属性表示查找支持的文件后缀,目前默认为下面4种:

    max_thread_cnt = 500  # 同时存活线程数最大500
    big_byte_file = 0.05  # MB
    ext_list = ['.csv', '.txt', '.md', '.py', '.java']

对外公开方法get_files
主要包括调用私有方法__get_files

同时,通过join,等待所有线程执行完成后,主线程才开始执行print('-' * 100)及以下代码:

    def get_files(self):
        self.__get_files(self.path)
        for thr in self.threads:
            thr.join()

        print('-' * 100)
        print(f'共使用{len(self.threads)}个线程')
        print(f'同时存活线程数{self._live_thread_cnt}')
        if len(self.result) == 0:
            print(self.text + " not found! ")
        else:
            for res in self.result:
                print(res)

递归私有方法__get_files
递归查找满足后缀的文件

同时存活线程数不能超过默认值,主要考虑内存的负载。

    def __get_files(self, path):
        files = os.listdir(path)
        for name in files:
            path_name = os.path.join(path, name)
            if os.path.isdir(path_name):
                self.__get_files(path_name)
            if self.__in_extensions(name):
                file_byte = os.stat(path_name).st_size / 1024 / 1024
                if file_byte > TextFinder.big_byte_file:
                    if threading.active_count() < TextFinder.max_thread_cnt:
                        big_file_proc = threading.Thread(target=self.__task, args=(path_name,))
                        big_file_proc.start()
                        self.threads.append(big_file_proc)
                        self._live_thread_cnt = max(self._live_thread_cnt, threading.active_count())
                    else:
                        self.__task(path_name)
                else:
                    self.__task(path_name)

线程内的target等于__task,open和read的IO操作适合Python的多线程处理,加快查找:

    def __task(self, path_name):
        print(f'正在查找 {path_name}')
        f = open(path_name, "r")
        try:
            if self.text in f.read():
                self.__found_flag = True
                self.result.append(f'{self.text} found in {path_name}')
        except UnicodeDecodeError:
            print(f'解析错误 {path_name}')

封装cmd命令行接口
click模块封装命令行接口的标准用法,如下所示:

@click.command()
@click.option('-t', help='要查找的文本')
@click.option('-p', help='在哪里查找')
def __cmd(t, p):
    TextFinder(t, p).get_files()


if __name__ == "__main__":
    cmd()

以上就是本次分享的所有内容,想要了解更多欢迎前往公众号:Python 编程学习圈,每日干货分享