基于Python開發(fā)高效文件搜索與內(nèi)容匹配工具
在日常的開發(fā)和辦公中,查找和篩選特定文件或文件內(nèi)容的需求十分常見。尤其是在處理大量文件時(shí),如何高效地搜索到所需內(nèi)容,往往是一個(gè)挑戰(zhàn)。今天,我們將分享一個(gè)基于PyQt6的文件搜索工具,它不僅能根據(jù)文件后綴進(jìn)行搜索,還支持關(guān)鍵字匹配和詳細(xì)信息展示。通過這款工具,您可以更高效地管理和查找文件,提升工作效率。
1.概述
這款基于PyQt6開發(fā)的文件搜索工具,具備文件搜索、文件內(nèi)容匹配、搜索結(jié)果顯示及導(dǎo)出功能。程序支持通過拖拽輸入框選擇目標(biāo)文件夾,并對其中的文件進(jìn)行后綴篩選和內(nèi)容搜索。匹配結(jié)果可以實(shí)時(shí)顯示在界面上,用戶可以查看匹配的文件名、大小以及路徑,還可以導(dǎo)出搜索結(jié)果或者樹結(jié)構(gòu)信息。
該工具的核心特點(diǎn)包括:
- 多線程搜索:使用PyQt的QThread進(jìn)行文件搜索操作,避免UI卡頓。
- 多種文件編碼支持:支持UTF-8、GBK等常見編碼格式,能夠準(zhǔn)確讀取文件內(nèi)容。
- 實(shí)時(shí)展示匹配結(jié)果:搜索結(jié)果實(shí)時(shí)更新,方便用戶查看和處理。
- 導(dǎo)入導(dǎo)出功能:支持導(dǎo)入文件樹信息及導(dǎo)出搜索結(jié)果。
2.功能使用
2.1 文件夾選擇與搜索設(shè)置
啟動(dòng)應(yīng)用后,用戶首先需要選擇一個(gè)目標(biāo)文件夾,輸入需要搜索的文件后綴以及關(guān)鍵詞。文件后綴支持通過分號分隔的多項(xiàng)選擇,例如 .txt; .log,而關(guān)鍵詞則是搜索文件內(nèi)容時(shí)所需要匹配的字符串。
界面上設(shè)計(jì)了拖拽功能,用戶可以直接將目標(biāo)文件夾拖拽到輸入框中,免去手動(dòng)輸入路徑的麻煩。
2.2 文件內(nèi)容搜索
輸入完搜索條件后,點(diǎn)擊“開始搜索”按鈕,程序會(huì)啟動(dòng)一個(gè)后臺(tái)線程(SearchWorker),在目標(biāo)文件夾中遞歸查找符合條件的文件。搜索的內(nèi)容不僅是文件名匹配,還包括文件內(nèi)部的內(nèi)容是否包含指定的關(guān)鍵字。程序采用了多種編碼方式進(jìn)行文件內(nèi)容的讀取,以確保文件格式不同的情況下能夠正確處理。
搜索過程中,UI界面并不會(huì)卡頓,用戶可以繼續(xù)操作其他功能。匹配到的文件會(huì)實(shí)時(shí)顯示在左側(cè)的文件樹結(jié)構(gòu)中,每一項(xiàng)顯示文件名、大小和路徑等信息。
2.3 單文件搜索
除了支持批量文件搜索外,程序還提供了單文件搜索的功能。在“單文件搜索”板塊中,用戶可以選擇指定的文件,輸入要查找的關(guān)鍵詞,點(diǎn)擊搜索按鈕后,程序會(huì)展示匹配到的行及其內(nèi)容,并在右側(cè)的文本框中顯示完整匹配結(jié)果。
該功能可以幫助開發(fā)人員快速定位特定代碼段或文件中的問題,極大提高了工作效率。
2.4 結(jié)果展示與導(dǎo)出
搜索結(jié)束后,程序會(huì)統(tǒng)計(jì)文件總數(shù)、匹配的行數(shù)和搜索所花費(fèi)的時(shí)間,展示在界面上。同時(shí),所有匹配到的結(jié)果會(huì)以列表的形式顯示在右側(cè),用戶可以選擇具體的行進(jìn)行查看。
如果用戶需要保存搜索結(jié)果,程序提供了導(dǎo)出功能。用戶可以將所有匹配的文件內(nèi)容導(dǎo)出為文本文件,方便后續(xù)查看或共享。另外,文件樹的結(jié)構(gòu)信息也可以導(dǎo)出,便于存檔和分析。
2.5 文件樹信息導(dǎo)入
程序還支持導(dǎo)入文件樹信息。用戶可以將之前導(dǎo)出的樹結(jié)構(gòu)文件導(dǎo)入到工具中,快速恢復(fù)之前的搜索狀態(tài)和文件結(jié)構(gòu)。導(dǎo)入后的文件樹可以繼續(xù)進(jìn)行搜索或查看,提升了工具的靈活性和可用性。
3.代碼結(jié)構(gòu)與實(shí)現(xiàn)
3.1 多線程處理
為了保證UI界面的流暢性,程序使用了PyQt的QThread來處理搜索任務(wù)。具體來說,SearchWorker類負(fù)責(zé)遍歷目標(biāo)文件夾并查找符合條件的文件,而FileReader類則負(fù)責(zé)讀取文件內(nèi)容并檢查是否包含指定的關(guān)鍵字。多線程的使用有效避免了UI在進(jìn)行文件搜索時(shí)的卡頓問題,讓用戶能夠在后臺(tái)進(jìn)行其他操作。
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):
# 搜索文件并匹配關(guān)鍵字
pass
3.2 文件編碼支持
文件內(nèi)容的讀取采用了多種常見編碼格式(如UTF-8、GBK等),以確保無論文件是用哪種編碼保存,程序都能正確地讀取其內(nèi)容。在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 導(dǎo)入導(dǎo)出功能
導(dǎo)出功能實(shí)現(xiàn)了將文件匹配結(jié)果和文件樹結(jié)構(gòu)導(dǎo)出為文本文件,支持用戶將數(shù)據(jù)保存在指定路徑。通過QFileDialog對話框,用戶可以選擇保存的位置或?qū)氲臄?shù)據(jù)文件。
def export_match_list(self):
# 導(dǎo)出匹配結(jié)果
pass
def import_tree_info(self):
# 導(dǎo)入文件樹
pass
4.效果展示

5.相關(guān)源碼
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("目標(biāo)文件夾:"), 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("搜索內(nèi)容:"), 2, 0)
top_layout.addWidget(self.keyword_input, 2, 1)
top_layout.addWidget(self.btn_search, 0, 3, 3, 1)
# 統(tǒng)計(jì)信息
stats_panel = QWidget()
stats_layout = QHBoxLayout(stats_panel)
self.file_count = QLabel("文件總數(shù): 0")
self.line_count = QLabel("匹配行數(shù): 0")
self.time_label = QLabel("耗時(shí): 0.00秒")
stats_layout.addWidget(self.file_count)
stats_layout.addWidget(self.line_count)
stats_layout.addWidget(self.time_label)
# 主內(nèi)容區(qū)域
content_panel = QWidget()
content_layout = QHBoxLayout(content_panel)
# 左側(cè)結(jié)果樹
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)
# 右側(cè)面板
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("導(dǎo)出結(jié)果")
self.btn_export_tree = QPushButton("導(dǎo)出樹信息")
self.btn_import_tree = QPushButton("導(dǎo)入樹信息")
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, "錯(cuò)誤", "請?zhí)顚懰兴阉鳁l件")
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"文件總數(shù): {self.tree.topLevelItemCount()}")
def on_search_finished(self):
self.btn_search.setEnabled(True)
elapsed = time.time() - self.start_time
self.time_label.setText(f"耗時(shí): {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, "錯(cuò)誤", "無效的文件路徑")
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"匹配行數(shù): {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"匹配行數(shù): {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, "錯(cuò)誤", "請輸入搜索關(guān)鍵字")
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"匹配行數(shù): {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, "導(dǎo)出完成", 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, "導(dǎo)出完成", f"樹結(jié)構(gòu)已保存到:{path}")
def import_tree_info(self):
path, _ = QFileDialog.getOpenFileName(self, "選擇導(dǎo)入文件", "", "文本文件 (*.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"文件總數(shù): {self.tree.topLevelItemCount()}")
def update_counts(self):
self.file_count.setText(f"文件總數(shù): {self.tree.topLevelItemCount()}")
self.line_count.setText(f"匹配行數(shù): {self.match_list.count()}")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
6.總結(jié)
這是一個(gè)基于PyQt6開發(fā)的文件搜索與內(nèi)容匹配工具。該工具不僅支持高效的文件查找,還能通過關(guān)鍵字匹配文件內(nèi)容,并提供詳細(xì)的匹配結(jié)果展示和導(dǎo)出功能。利用PyQt的多線程特性,程序在執(zhí)行搜索任務(wù)時(shí)能夠保持界面的響應(yīng)性,大大提升了用戶體驗(yàn)。
未來的改進(jìn)方向包括:
增強(qiáng)搜索效率:可以增加更多的文件搜索選項(xiàng),如模糊匹配或正則表達(dá)式支持,以滿足更復(fù)雜的查找需求。
界面優(yōu)化:加入進(jìn)度條或其他UI元素,實(shí)時(shí)反饋搜索進(jìn)度。
功能擴(kuò)展:支持更多的文件操作,如批量重命名、批量刪除等。
以上就是基于Python開發(fā)高效文件搜索與內(nèi)容匹配工具的詳細(xì)內(nèi)容,更多關(guān)于Python文件搜索與內(nèi)容匹配的資料請關(guān)注腳本之家其它相關(guān)文章!
- Python pyttsx3庫實(shí)現(xiàn)文本轉(zhuǎn)語音功能的示例
- Python調(diào)用JAR包的類和方法詳細(xì)指南
- Python實(shí)現(xiàn)文件/文件夾復(fù)制功能
- Python實(shí)現(xiàn)無痛修改第三方庫源碼的方法詳解
- Python如何使用__slots__實(shí)現(xiàn)節(jié)省內(nèi)存和性能優(yōu)化
- Python+PyQt5實(shí)現(xiàn)多屏幕協(xié)同播放功能
- Python中隨機(jī)休眠技術(shù)原理與應(yīng)用詳解
- Python模塊Uvicorn實(shí)戰(zhàn)
相關(guān)文章
Python自動(dòng)化完成tb喵幣任務(wù)的操作方法
2019雙十一,tb推出了新的活動(dòng),商店喵幣,看了一下每天都有幾個(gè)任務(wù)來領(lǐng)取喵幣,從而升級店鋪賺錢,然而我既想賺紅包又不想干苦力,遂使用python來進(jìn)行手機(jī)自動(dòng)化操作,需要的朋友跟隨小編一起看看吧2019-10-10
python抓取網(wǎng)頁內(nèi)容并進(jìn)行語音播報(bào)的方法
今天小編就為大家分享一篇python抓取網(wǎng)頁內(nèi)容并進(jìn)行語音播報(bào)的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12
python3+telnetlib實(shí)現(xiàn)簡單自動(dòng)測試示例詳解
telnetlib 模塊提供一個(gè)實(shí)現(xiàn)Telnet協(xié)議的類 Telnet,本文重點(diǎn)給大家介紹python3+telnetlib實(shí)現(xiàn)簡單自動(dòng)測試示例詳解,需要的朋友可以參考下2021-08-08
詳解Python計(jì)算機(jī)視覺 圖像扭曲(仿射扭曲)
這篇文章主要介紹了Python計(jì)算機(jī)視覺 圖像扭曲(仿射扭曲),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03

