基础搜索方法:

用 pathlib 库搜索文件用 Python 搜索文件时需要使用 pathlib 库的 glob() 函数和 rglob() 函数,glob() 函数可以实现基于文件名的搜索方法,rglob 函数可以实现基于扩展名的搜索方法。


from pathlib import Path

base_dir = '/Users/edz/Desktop/'
keywords = '**/*BBC*'

# 遍历base_dir指向的目录下所有的文件
p = Path(base_dir)

# 当前目录下包含BBC的所有文件名称
files = p.glob(keywords)  
# files的类型是迭代器
# 通过list()函数转换为列表输出
# print(list(files))

# xlsx结尾的文件
files2 = p.rglob('*.xlsx')
print(list(files2))

# 遍历子目录和所有文件
files3 = p.glob('**/*')
print(list(files3))

由于 glob() 进行匹配的是文件的路径和文件名称方式,如: “c:\somepath\to\filename_include_BBC_voice.exe” , 而我们进行文件搜索的时候一般会使用关键字,如“BBC”,因此在搜索时我们需要为关键字加上通配符的形式,如“BBC” 。

通配符是类似正则表达式的元字符的一种特殊符号,它不能用在正则表达式中,只能用在 glob(全称 global)匹配模式中。
在这里插入图片描述
rglob 函数是从文件路径末尾向前进行匹配的,这是它和 glob() 函数的主要区别, 基于 rglob() 函数的搜索顺序特点,经常被我们用于进行扩展名的搜索,比如说采用 rglob(’*.xlsx’) 就可以搜索所有的 xlsx 扩展名文件,要比使用 glob() 编写的模式匹配更简单,参数的含义也更清晰。

最后再看一下 glob() 和 rglob() 函数的返回值,有一点我需要提醒你:它们的执行结果是我们之前课程中没有接触过的一种新的数据类型,这种类型叫做“迭代器”。

提升搜索效率的两种方法

用 Python 的 pathlib 库实现文件搜索,只是在灵活性上比 Windows 默认的搜索更好,但是搜索效率上并不能带来任何提高。为了减少搜索的等待时间, 接下来,我就教你使用指定搜索路径和建立索引文件两个方法, 提高 pathlib 库的搜索效率。

指定搜索路径

我们先来看第一种,指定搜索路径。
我们需要通过三个步骤实现:

先生成配置文件,把要搜索的路径写入到配置文件中;

再编写读取配置文件和搜索的自定义函数,把配置文件中的路径读取出来,逐个目录搜索;

最后,将多个目录的搜索结果合并输出,便于你通过结果快速找到自己想要的文件。

先说第一步,怎么使用 Python 读取配置文件。以往我们会把要搜索的路径写入到变量,并把定义路径的变量名称放在代码前几行的位置,便于下次修改搜索目录的时候找到这个变量。但是对于代码工程稍微复杂的程序来说,往往会有多个代码文件,仍然不利于每次搜索的时候进行搜索路径的修改。新的方法,就是把变量放入到一个单独的文件中,这个文件被称作该代码的配置文件。

这种方法的好处是你修改搜索目录时不用打开代码文件。假设你的朋友也需要类似功能,那你就可以把代码和配置文件一起发给他,哪怕他完全不会 Python,也能使用你编写的程序实现高效搜索。

配置文件一般为文本文件。配置文件的格式,一般由软件作者基于软件的功能和自己的习惯来指定,不过也有通用的配置文件格式。

比如在 Windows 系统中,最常见的配置文件是扩展名为.ini 的文件,在今天这节课,我们就把.ini 文件格式作为配置文件的标准格式。.ini 文件格式包含三个部分,分别是节、参数和注释。格式如下:

[section]
参数
(键=值)
  name=value
注释 
注释使用“;”分号表示。在分号后面的文字,直到该行结尾都全部为注解。
;注释内容
#基于.ini 文件的格式,我把配置搜索路径的配置文件修改为如下:
[work]
;工作文件保存路径
searchpath=/Users/edz,/tmp

[game]
;娱乐文件保存路径
searchpath=/games,/movies,/music

在这段代码中,我设置了 work 和 game 两个“节”,分别代表工作和娱乐。这样设置的好处是,我可以根据不同的用途来搜索不同的目录。如果搜索时使用了更少的目录,也会相应减少搜索的等待时间。

另外,你会发现两个“节”中的参数我都指定成相同的名字 --searchpath,这样设置的好处是我将搜索范围从“工作”改为“娱乐”时,只需要在代码里修改搜索的“节”,不用修改搜索的参数。

除了“节”和“参数”,在配置文件中,你还应该关注我对参数 searchpath 设置值的方式,它的值是我想要进行搜索的路径范围,为了在程序中能够更方便得读取多个路径,我使用逗号来分隔多个路径。

找到 search.ini 文件完整路径之后,接下来需要读取并分析.ini 文件格式,Python 有实现这个功能的的库,它叫做 configparser 库,通过这个库你可以直接读取.ini 文件中的 searchpath 参数,不用通过 read() 函数读取文件内容,手动编写分析.ini 文件的脚本了。


import configparser
import pathlib 
from pathlib import Path

def read_dirs(ini_filename, section, arg):
    """
    通过ini文件名,节和参数取得要操作的多个目录
    """
    current_path = pathlib.PurePath(__file__).parent
    inifile = current_path.joinpath(ini_filename)

    # cf是类ConfigParser的实例
    cf = configparser.ConfigParser()

    # 读取.ini文件
    cf.read(inifile)

    # 读取work节 和 searchpath参数 
    return cf.get(section, arg).split(",")

def locate_file(base_dir, keywords):
    p = Path(base_dir)
    files = p.glob(keywords) 
    return list(files)


dirs = read_dirs('search.ini', 'work', 'searchpath')
# ['/Users/edz', '/tmp']
keywords = '**/*BBC*'

# 定义存放查找结果的列表
result = []

# 从每个文件夹中搜索文件
for dir in dirs:
    files = locate_file(dir, keywords)
    result += files

# 将PosixPath转为字符串
print( [str(r) for r in result] )

read_dirs() 函数实现了读取.ini 文件,并将返回的多个路径处理为列表类型。列表类型适合多组并列的数据,多个目录刚好可以使用列表这种数据类型来存放要搜索的目录名称。

locate_file() 函数通过代码的第 35 行循环功能,对每个目录进行了搜索,并将搜索的结果存入 result 变量。result 变量是一个列表数据类型,由于搜索到的文件可能包含多个匹配的文件路径,我需要将搜索到的结果依次存入 result 列表中,再继续搜索下一个目录,继续通过 append() 函数将结果放入列表,直到所有的目录搜索完成,整个搜索的程序才真正执行结束。

最后还有一点需要你注意,在进行路径处理的过程中,pathlib 库为了规避不同操作系统路径写法的差异,就把路径统一定义为 PosixPath() 对象。因此,你在使用这些路径的时候,需要先将 PosixPath 对象转换为字符串类型。我在代码最后一行通过 Python 内置函数 str() 函数把 PosixPath 对象逐个转换为字符串类型,并再次存入到列表当中。

建立索引文件

我们可以基于指定搜索路径的程序进行改造:先把配置文件目录下所有文件路径的保存方式由列表改为文件;再把搜索功能改为从文件搜索。


def locate_file(base_dir, keywords='**/*'):
    """
    迭代目录下所有文件
    """
    p = Path(base_dir)
    return p.glob(keywords)

def write_to_db():
    """
    写入索引文件
    """
    current_path = pathlib.PurePath(__file__).parent
    dbfile = current_path.joinpath("search.db")

    with open(dbfile, 'w', encoding='utf-8') as f:
        for r in result:
            f.write(f"{str(r)}\n")

# 读取配置文件
dirs = read_dirs('search.ini', 'work', 'searchpath')

# 遍历目录
result = []
for dir in dirs:
    for files in locate_file(dir):
        result.append(files)

# 将目录写入索引文件
write_to_db()

在代码中我增加了 write_to_db() 函数,它在代码的第 16-18 行,我通过写入文件方式替代了写入列表的功能。同时,为了能遍历所有的目录,我还修改了 locate_file() 函数的第二个参数,将它改为“keywords='/*'”。通过这两处的修改,就把所有文件路径全部保存到 search.db 文件中了。**

search.db 的文件内容如下,这里记录了配置文件指定的所有目录下的所有文件路径:


/tmp/com.apple.launchd.kZENgZTtVz
/tmp/com.google.Keystone
/tmp/mysql.sock
/tmp/com.adobe.AdobeIPCBroker.ctrl-edz
/tmp/com.apple.launchd.kZENgZTtVz/Listeners
/tmp/com.google.Keystone/.keystone_install_lock
... ...

从文本中搜索关键字


import pathlib 
import re

keyword = "apple"

# 获取索引文件路径
current_path = pathlib.PurePath(__file__).parent
dbfile = current_path.joinpath("search.db")

# 在索引文件中搜索关键字
with open(dbfile, encoding='utf-8') as f:
    for line in f.readlines():
        if re.search(keyword, line):
            print(line.rstrip())

在代码中我利用正则表达式的 re.search() 搜索函数,以 keyword 变量作为搜索的关键字,对 search.db 索引文件的每一行进行了匹配,最后将符合关键字“apple”的文件路径和名称一起显示在屏幕上。

使用这种方式来搜索文件,要比使用操作系统自带的搜索工具快得多,因为我将原本 Windows 搜索硬盘上的文件所消耗的时间拆分成了两部分。一部分是 updatedb.py 建立索引的时间;一部分是从 search.db 索引文件查找关键字的时间。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐