开发者

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

目录
  • 1.概述
  • 2.功能使用
    • 2.1 文件夹选择与搜索设置
    • 2.2 文件内容搜索
    • 2.3 单文件搜索
    • 2.4 结果展示与导出
    • 2.5 文件树信息导入
  • 3.代码结构与实现
    • 3.1 多线程处理
    • 3.2 文件编码支持
    • 3.3 导入导出功能
  • 4.效果展示
    • 5.相关源码
      • 6.总结

        在日常的开发和办公中,查找和筛选特定文件或文件内容的需求十分常见。尤其是在处理大量文件时,如何高效地搜索到所需内容,往往是一个挑战。今天,我们将分享一个基于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.效果展示

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

        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 (UnicphpodeDecodeError, 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 =php 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_filepythons_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(selandroidf.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文件搜索与内容匹配的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜