当前位置: 代码网 > it编程>前端脚本>Python > 基于Python开发高效文件搜索与内容匹配工具

基于Python开发高效文件搜索与内容匹配工具

2025年03月31日 Python 我要评论
在日常的开发和办公中,查找和筛选特定文件或文件内容的需求十分常见。尤其是在处理大量文件时,如何高效地搜索到所需内容,往往是一个挑战。今天,我们将分享一个基于pyqt6的文件搜索工具,它不仅能根据文件后

在日常的开发和办公中,查找和筛选特定文件或文件内容的需求十分常见。尤其是在处理大量文件时,如何高效地搜索到所需内容,往往是一个挑战。今天,我们将分享一个基于pyqt6的文件搜索工具,它不仅能根据文件后缀进行搜索,还支持关键字匹配和详细信息展示。通过这款工具,您可以更高效地管理和查找文件,提升工作效率。

1.概述

这款基于pyqt6开发的文件搜索工具,具备文件搜索、文件内容匹配、搜索结果显示及导出功能。程序支持通过拖拽输入框选择目标文件夹,并对其中的文件进行后缀筛选和内容搜索。匹配结果可以实时显示在界面上,用户可以查看匹配的文件名、大小以及路径,还可以导出搜索结果或者树结构信息。

该工具的核心特点包括:

  • 多线程搜索:使用pyqt的qthread进行文件搜索操作,避免ui卡顿。
  • 多种文件编码支持:支持utf-8、gbk等常见编码格式,能够准确读取文件内容。
  • 实时展示匹配结果:搜索结果实时更新,方便用户查看和处理。
  • 导入导出功能:支持导入文件树信息及导出搜索结果。

2.功能使用

2.1 文件夹选择与搜索设置

启动应用后,用户首先需要选择一个目标文件夹,输入需要搜索的文件后缀以及关键词。文件后缀支持通过分号分隔的多项选择,例如 .txt; .log,而关键词则是搜索文件内容时所需要匹配的字符串。

界面上设计了拖拽功能,用户可以直接将目标文件夹拖拽到输入框中,免去手动输入路径的麻烦。

2.2 文件内容搜索

输入完搜索条件后,点击“开始搜索”按钮,程序会启动一个后台线程(searchworker),在目标文件夹中递归查找符合条件的文件。搜索的内容不仅是文件名匹配,还包括文件内部的内容是否包含指定的关键字。程序采用了多种编码方式进行文件内容的读取,以确保文件格式不同的情况下能够正确处理。

搜索过程中,ui界面并不会卡顿,用户可以继续操作其他功能。匹配到的文件会实时显示在左侧的文件树结构中,每一项显示文件名、大小和路径等信息。

2.3 单文件搜索

除了支持批量文件搜索外,程序还提供了单文件搜索的功能。在“单文件搜索”板块中,用户可以选择指定的文件,输入要查找的关键词,点击搜索按钮后,程序会展示匹配到的行及其内容,并在右侧的文本框中显示完整匹配结果。

该功能可以帮助开发人员快速定位特定代码段或文件中的问题,极大提高了工作效率。

2.4 结果展示与导出

搜索结束后,程序会统计文件总数、匹配的行数和搜索所花费的时间,展示在界面上。同时,所有匹配到的结果会以列表的形式显示在右侧,用户可以选择具体的行进行查看。

如果用户需要保存搜索结果,程序提供了导出功能。用户可以将所有匹配的文件内容导出为文本文件,方便后续查看或共享。另外,文件树的结构信息也可以导出,便于存档和分析。

2.5 文件树信息导入

程序还支持导入文件树信息。用户可以将之前导出的树结构文件导入到工具中,快速恢复之前的搜索状态和文件结构。导入后的文件树可以继续进行搜索或查看,提升了工具的灵活性和可用性。

3.代码结构与实现

3.1 多线程处理

为了保证ui界面的流畅性,程序使用了pyqt的qthread来处理搜索任务。具体来说,searchworker类负责遍历目标文件夹并查找符合条件的文件,而filereader类则负责读取文件内容并检查是否包含指定的关键字。多线程的使用有效避免了ui在进行文件搜索时的卡顿问题,让用户能够在后台进行其他操作。

class searchworker(qthread):
    update_file = pyqtsignal(dict)
    finished = pyqtsignal()

    def __init__(self, folder, extensions, keyword):
        super().__init__()
        self.folder = folder
        self.extensions = extensions
        self.keyword = keyword

    def run(self):
        # 搜索文件并匹配关键字
        pass

3.2 文件编码支持

文件内容的读取采用了多种常见编码格式(如utf-8、gbk等),以确保无论文件是用哪种编码保存,程序都能正确地读取其内容。在filereader类中,我们尝试了不同的编码方式,直到成功读取文件为止。

def file_contains_keyword(self, path, keyword):
for encoding in [‘utf-8’, ‘gbk’, ‘latin-1’]:
try:
with open(path, ‘r’, encoding=encoding) as f:
return any(keyword in line for line in f)
except (unicodedecodeerror, exception):
continue
return false

3.3 导入导出功能

导出功能实现了将文件匹配结果和文件树结构导出为文本文件,支持用户将数据保存在指定路径。通过qfiledialog对话框,用户可以选择保存的位置或导入的数据文件。

def export_match_list(self):
    # 导出匹配结果
    pass

def import_tree_info(self):
    # 导入文件树
    pass

4.效果展示

5.相关源码

import sys
import os
import time
from pyqt6.qtwidgets import (
    qapplication, qmainwindow, qwidget, qvboxlayout, qhboxlayout, qgridlayout,
    qlabel, qlineedit, qpushbutton, qtreewidget, qtreewidgetitem,
    qlistwidget, qtextedit, qscrollarea, qfiledialog, qmessagebox
)
from pyqt6.qtcore import qt, qthread, pyqtsignal, qmimedata, qsize
from pyqt6.qtgui import qdragenterevent, qdropevent
 
class draggablelineedit(qlineedit):
    def __init__(self, parent=none):
        super().__init__(parent)
        self.setacceptdrops(true)
 
    def dragenterevent(self, event: qdragenterevent):
        if event.mimedata().hasurls():
            event.acceptproposedaction()
 
    def dropevent(self, event: qdropevent):
        urls = event.mimedata().urls()
        if urls:
            path = urls[0].tolocalfile()
            if os.path.isdir(path) or os.path.isfile(path):
                self.settext(path)
 
class searchworker(qthread):
    update_file = pyqtsignal(dict)
    finished = pyqtsignal()
     
    def __init__(self, folder, extensions, keyword):
        super().__init__()
        self.folder = folder
        self.extensions = extensions
        self.keyword = keyword
        self.running = true
 
    def run(self):
        for root, _, files in os.walk(self.folder):
            if not self.running: break
            for file in files:
                if any(file.endswith(ext) for ext in self.extensions):
                    path = os.path.join(root, file)
                    if self.file_contains_keyword(path, self.keyword):
                        size = os.path.getsize(path)
                        self.update_file.emit({
                            "name": file,
                            "size": self.format_size(size),
                            "path": path
                        })
        self.finished.emit()
 
    def file_contains_keyword(self, path, keyword):
        for encoding in ['utf-8', 'gbk', 'latin-1']:
            try:
                with open(path, 'r', encoding=encoding) as f:
                    return any(keyword in line for line in f)
            except (unicodedecodeerror, exception):
                continue
        return false
 
    def format_size(self, size):
        if size < 1024:
            return f"{size} b"
        elif size < 1024 * 1024:
            return f"{size/1024:.1f} kb"
        else:
            return f"{size/(1024 * 1024):.1f} mb"
 
class filereader(qthread):
    update_line = pyqtsignal(str)
    finished = pyqtsignal()
     
    def __init__(self, path, keyword):
        super().__init__()
        self.path = path
        self.keyword = keyword
        self.results = []
 
    def run(self):
        self.results = []
        for encoding in ['utf-8', 'gbk', 'latin-1']:
            try:
                with open(self.path, 'r', encoding=encoding) as f:
                    for i, line in enumerate(f, 1):
                        if self.keyword in line:
                            text = f"line {i}: {line.strip()[:50]}"
                            self.update_line.emit(text)
                            self.results.append(line.strip())
                break
            except (unicodedecodeerror, exception):
                continue
        self.finished.emit()
 
class allfilesreader(qthread):
    update_line = pyqtsignal(str)
    finished = pyqtsignal()
     
    def __init__(self, paths, keyword):
        super().__init__()
        self.paths = paths
        self.keyword = keyword
 
    def run(self):
        for path in self.paths:
            if not os.path.isfile(path):
                continue
            reader = filereader(path, self.keyword)
            reader.update_line.connect(self.update_line.emit)
            reader.start()
            reader.wait()
        self.finished.emit()
 
class mainwindow(qmainwindow):
    def __init__(self):
        super().__init__()
        self.current_matches = []
        self.search_thread = none
        self.file_reader = none
        self.start_time = 0
        self.init_ui()
 
    def init_ui(self):
        self.setwindowtitle("pyqt6文件搜索工具")
        self.setgeometry(100, 100, 1200, 700)
 
        main_widget = qwidget()
        self.setcentralwidget(main_widget)
        main_layout = qvboxlayout(main_widget)
 
        # 顶部控制面板
        top_panel = qwidget()
        top_layout = qgridlayout(top_panel)
         
        self.folder_input = draggablelineedit()
        self.ext_input = qlineedit(".txt")
        self.keyword_input = qlineedit()
        self.btn_search = qpushbutton("开始搜索")
        self.btn_search.clicked.connect(self.start_search)
 
        top_layout.addwidget(qlabel("目标文件夹:"), 0, 0)
        top_layout.addwidget(self.folder_input, 0, 1)
        top_layout.addwidget(self.create_browse_btn(), 0, 2)
        top_layout.addwidget(qlabel("文件后缀:"), 1, 0)
        top_layout.addwidget(self.ext_input, 1, 1)
        top_layout.addwidget(qlabel("搜索内容:"), 2, 0)
        top_layout.addwidget(self.keyword_input, 2, 1)
        top_layout.addwidget(self.btn_search, 0, 3, 3, 1)
 
        # 统计信息
        stats_panel = qwidget()
        stats_layout = qhboxlayout(stats_panel)
        self.file_count = qlabel("文件总数: 0")
        self.line_count = qlabel("匹配行数: 0")
        self.time_label = qlabel("耗时: 0.00秒")
        stats_layout.addwidget(self.file_count)
        stats_layout.addwidget(self.line_count)
        stats_layout.addwidget(self.time_label)
 
        # 主内容区域
        content_panel = qwidget()
        content_layout = qhboxlayout(content_panel)
 
        # 左侧结果树
        self.tree = qtreewidget()
        self.tree.setheaderlabels(["文件名", "大小", "路径"])
        self.tree.setcolumnwidth(0, 250)
        self.tree.setcolumnwidth(1, 100)
        self.tree.doubleclicked.connect(self.on_tree_double_click)
        tree_scroll = qscrollarea()
        tree_scroll.setwidgetresizable(true)
        tree_scroll.setwidget(self.tree)
 
        # 右侧面板
        right_panel = qwidget()
        right_layout = qvboxlayout(right_panel)
 
        # 按钮面板
        btn_panel = qwidget()
        btn_layout = qhboxlayout(btn_panel)
        self.btn_search_all = qpushbutton("搜索所有文件")
        self.btn_export_matches = qpushbutton("导出结果")
        self.btn_export_tree = qpushbutton("导出树信息")
        self.btn_import_tree = qpushbutton("导入树信息")
        btn_layout.addwidget(self.btn_search_all)
        btn_layout.addwidget(self.btn_export_matches)
        btn_layout.addwidget(self.btn_export_tree)
        btn_layout.addwidget(self.btn_import_tree)
 
        # 单文件搜索
        single_panel = qwidget()
        single_layout = qhboxlayout(single_panel)
        self.single_input = draggablelineedit()
        btn_single = qpushbutton("搜索")
        btn_single.clicked.connect(self.single_file_search)
        single_layout.addwidget(qlabel("单文件搜索:"))
        single_layout.addwidget(self.single_input)
        single_layout.addwidget(btn_single)
 
        # 匹配列表
        self.match_list = qlistwidget()
        self.match_list.doubleclicked.connect(self.on_list_double_click)
        list_scroll = qscrollarea()
        list_scroll.setwidgetresizable(true)
        list_scroll.setwidget(self.match_list)
 
        # 详情文本框
        self.detail_text = qtextedit()
        self.detail_text.setreadonly(true)
 
        right_layout.addwidget(single_panel)
        right_layout.addwidget(btn_panel)
        right_layout.addwidget(list_scroll)
        right_layout.addwidget(self.detail_text)
 
        content_layout.addwidget(tree_scroll)
        content_layout.addwidget(right_panel)
 
        main_layout.addwidget(top_panel)
        main_layout.addwidget(stats_panel)
        main_layout.addwidget(content_panel)
 
        # 连接新按钮信号
        self.btn_search_all.clicked.connect(self.search_all_files)
        self.btn_export_matches.clicked.connect(self.export_match_list)
        self.btn_export_tree.clicked.connect(self.export_tree_info)
        self.btn_import_tree.clicked.connect(self.import_tree_info)
 
    def create_browse_btn(self):
        btn = qpushbutton("浏览")
        btn.clicked.connect(self.browse_folder)
        btn.setfixedsize(qsize(80, 30))
        return btn
 
    def browse_folder(self):
        path = qfiledialog.getexistingdirectory(self, "选择文件夹")
        if path:
            self.folder_input.settext(path)
 
    def start_search(self):
        if self.search_thread and self.search_thread.isrunning():
            return
 
        folder = self.folder_input.text()
        exts = self.ext_input.text().strip().split(";")
        keyword = self.keyword_input.text().strip()
 
        if not all([folder, exts, keyword]):
            qmessagebox.critical(self, "错误", "请填写所有搜索条件")
            return
 
        self.tree.clear()
        self.match_list.clear()
        self.current_matches = []
        self.update_counts()
        self.start_time = time.time()
 
        self.search_thread = searchworker(folder, exts, keyword)
        self.search_thread.update_file.connect(self.add_file_result)
        self.search_thread.finished.connect(self.on_search_finished)
        self.search_thread.start()
        self.btn_search.setenabled(false)
 
    def add_file_result(self, data):
        item = qtreewidgetitem()
        item.settext(0, data["name"])
        item.settext(1, data["size"])
        item.settext(2, data["path"])
        self.tree.addtoplevelitem(item)
        self.file_count.settext(f"文件总数: {self.tree.toplevelitemcount()}")
 
    def on_search_finished(self):
        self.btn_search.setenabled(true)
        elapsed = time.time() - self.start_time
        self.time_label.settext(f"耗时: {elapsed:.2f}秒")
 
    def single_file_search(self):
        path = self.single_input.text()
        keyword = self.keyword_input.text().strip()
 
        if not os.path.isfile(path):
            qmessagebox.critical(self, "错误", "无效的文件路径")
            return
 
        self.match_list.clear()
        self.current_matches = []
        self.file_reader = filereader(path, keyword)
        self.file_reader.update_line.connect(self.match_list.additem)
        self.file_reader.finished.connect(lambda: (
            self.line_count.settext(f"匹配行数: {self.match_list.count()}"),
            self.current_matches.extend(self.file_reader.results)
        ))
        self.file_reader.start()
 
    def on_tree_double_click(self):
        item = self.tree.currentitem()
        if not item: return
         
        path = item.text(2)
        keyword = self.keyword_input.text().strip()
        self.match_list.clear()
        self.current_matches = []
         
        self.file_reader = filereader(path, keyword)
        self.file_reader.update_line.connect(self.match_list.additem)
        self.file_reader.finished.connect(lambda: (
            self.line_count.settext(f"匹配行数: {self.match_list.count()}"),
            self.current_matches.extend(self.file_reader.results)
        ))
        self.file_reader.start()
 
    def on_list_double_click(self):
        index = self.match_list.currentrow()
        if 0 <= index < len(self.current_matches):
            self.detail_text.setplaintext(self.current_matches[index])
 
    def search_all_files(self):
        paths = []
        root = self.tree.invisiblerootitem()
        for i in range(root.childcount()):
            item = root.child(i)
            paths.append(item.text(2))
 
        keyword = self.keyword_input.text().strip()
        if not keyword:
            qmessagebox.critical(self, "错误", "请输入搜索关键字")
            return
 
        self.match_list.clear()
        self.current_matches = []
        self.all_files_reader = allfilesreader(paths, keyword)
        self.all_files_reader.update_line.connect(self.match_list.additem)
        self.all_files_reader.finished.connect(lambda: (
            self.line_count.settext(f"匹配行数: {self.match_list.count()}"),
            self.current_matches.extend(self.file_reader.results) if self.file_reader else none
        ))
        self.all_files_reader.start()
 
    def export_match_list(self):
        keyword = self.keyword_input.text().strip() or "search"
        timestamp = time.strftime("%y%m%d_%h%m%s")
        filename = f"{keyword}_{timestamp}.txt"
        desktop = f"d:/桌面/"
        path = os.path.join(desktop, filename)
 
        with open(path, 'w', encoding='utf-8') as f:
            for i in range(self.match_list.count()):
                f.write(self.match_list.item(i).text() + "\n")
 
        qmessagebox.information(self, "导出完成", f"文件已保存到:{path}")
 
    def export_tree_info(self):
        items = []
        root = self.tree.invisiblerootitem()
        for i in range(root.childcount()):
            item = root.child(i)
            items.append("\t".join([
                item.text(0),
                item.text(1),
                item.text(2)
            ]))
 
        timestamp = time.strftime("%y%m%d_%h%m%s")
        filename = f"tree_export_{timestamp}.txt"
        desktop = f"d:/桌面/"
        path = os.path.join(desktop, filename)
 
        with open(path, 'w', encoding='utf-8') as f:
            f.write("\n".join(items))
 
        qmessagebox.information(self, "导出完成", f"树结构已保存到:{path}")
 
    def import_tree_info(self):
        path, _ = qfiledialog.getopenfilename(self, "选择导入文件", "", "文本文件 (*.txt)")
        if not path:
            return
 
        self.tree.clear()
        with open(path, 'r', encoding='utf-8') as f:
            for line in f:
                parts = line.strip().split('\t')
                if len(parts) != 3:
                    continue
                item = qtreewidgetitem()
                item.settext(0, parts[0])
                item.settext(1, parts[1])
                item.settext(2, parts[2])
                self.tree.addtoplevelitem(item)
        self.file_count.settext(f"文件总数: {self.tree.toplevelitemcount()}")
 
    def update_counts(self):
        self.file_count.settext(f"文件总数: {self.tree.toplevelitemcount()}")
        self.line_count.settext(f"匹配行数: {self.match_list.count()}")
 
if __name__ == "__main__":
    app = qapplication(sys.argv)
    window = mainwindow()
    window.show()
    sys.exit(app.exec())

6.总结

这是一个基于pyqt6开发的文件搜索与内容匹配工具。该工具不仅支持高效的文件查找,还能通过关键字匹配文件内容,并提供详细的匹配结果展示和导出功能。利用pyqt的多线程特性,程序在执行搜索任务时能够保持界面的响应性,大大提升了用户体验。

未来的改进方向包括:

增强搜索效率:可以增加更多的文件搜索选项,如模糊匹配或正则表达式支持,以满足更复杂的查找需求。

界面优化:加入进度条或其他ui元素,实时反馈搜索进度。

功能扩展:支持更多的文件操作,如批量重命名、批量删除等。

以上就是基于python开发高效文件搜索与内容匹配工具的详细内容,更多关于python文件搜索与内容匹配的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com