宝宝丷 发表于 2024-5-29 16:49

批量替换文本文件 新增对 office 文档处理

本帖最后由 宝宝丷 于 2024-7-8 10:02 编辑


# 批量替换工具使用指南
本工具用于在文本文件、Word 文档和 Excel 表格中批量替换字符串。以下是使用该工具的详细指南。

## 安装依赖
在使用本工具之前,您需要确保你已经安装了Python,并安装以下依赖库::
```
# 依赖列表

## 核心依赖

- **Python**: 程序的运行环境。
- **Tkinter**: 用于构建图形用户界面(GUI)。
- **re**: 提供正则表达式的支持,用于复杂的字符串匹配与替换操作。

## 文件操作

- **os**: 用于处理文件和目录操作,如遍历文件夹和文件路径管理。
- **python-docx**: 用于操作 Word 文档(.docx)文件。
- **openpyxl**: 用于操作 Excel 工作簿(.xlsx)文件。

## 对话框和消息框

- **tkinter.filedialog**: 提供文件选择对话框,用于选择目录和文件。
- **tkinter.messagebox**: 用于显示警告、错误等消息。

## 其他

- **ttk**: Tkinter 的扩展组件库,提供更多的样式化组件。

```
## 功能特性
- **支持文件类型**:支持 .txt、.docx 和 .xlsx 文件的字符串批量替换。
- **正则表达式**:支持使用正则表达式进行复杂的替换模式。
- **备份功能**:可选是否为修改前的文件创建备份。

## 使用界面介绍
工具界面包括以下部分:

**1. 目录选择**:选择需要进行批量替换操作的文件夹。
**2. 旧字符串输入框**:输入需要被替换的字符串或正则表达式。
**3. 新字符串输入框**:输入新的字符串或正则表达式的替换结果。
**4. 使用正则表达式选择**:选择是否使用正则表达式替换。
**5. 创建备份复选框**:选择是否为每个被修改的文件创建备份。
**6. 日志显示区域**:显示操作的详细日志,包括成功或失败的信息。
**7. 进度条和进度信息**:显示当前的替换进度。
**8. 开始按钮**:点击开始执行批量替换操作。
## 操作步骤
**1. 启动程序**:运行 Python 脚本启动用户界面。
**2. 选择目录**:点击“选择目录”按钮,选择包含目标文件的文件夹。
**3. 输入字符串**:在“旧字符串”输入框中输入想要替换的内容,然后在“新字符串”输入框中输入替换后的内容。
**4. 设置选项**:

- **若需使用正则表达式,请在“使用正则表达式”下拉菜单中选择“是”。**
- **若需备份文件,请确保“创建备份”复选框被勾选。**

**5. 开始替换**:点击“开始替换”按钮,程序将遍历所选目录中的所有支持的文件,并执行替换操作。
**6. 查看日志和进度**:在界面下方的日志框中查看操作日志,进度条显示替换进度。

## 注意事项
- **使用正则表达式时,请确保您的表达式正确无误,以免发生不预期的替换。**
- **创建备份可以防止数据丢失,特别是在处理重要文件时建议开启此功能。**
- **确保文件在替换过程中不被其他程序占用,这可能导致替换失败。**
- **通过以上步骤,您可以方便地使用本工具进行文件内容的批量替换,无论是简单的字符串替换还是复杂的正则表达式替换。如果遇到任何问题,可参考日志- 信息排查或寻求技术支持。**

## 更新日志
### v1.2.0 - 最新更新
- **新增功能:支持 .docx 和 .xlsx 文件的字符串批量替换。**
- **性能优化:改进了文件处理的效率,减少内存使用。**

### v1.1.0
- **新增功能:添加了正则表达式替换支持。**
- **用户界面改进:优化了用户界面的响应性和可用性。**

### v1.0.0
- **初始发布:支持基本的文本文件.txt中的字符串批量替换。**
- **基础功能:包括文件夹选择、字符串输入、备份选项和进度条显示。**

```
import re
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from tkinter.scrolledtext import ScrolledText
from docx import Document
from openpyxl import load_workbook

class BatchReplaceApp:
    def __init__(self, root):
      self.root = root
      self.root.title("批量替换工具")
      self.root.geometry("900x800")
      self.root.configure(bg='#F0F0F0')

      self.style = ttk.Style()
      self.style.configure('TButton', font=('Segoe UI', 10), padding=6)
      self.style.configure('TEntry', font=('Segoe UI', 10))
      self.style.configure('TLabel', font=('Segoe UI', 10), background='#F0F0F0')
      self.style.configure('TCheckbutton', font=('Segoe UI', 10), background='#F0F0F0')

      self.create_widgets()

    def create_widgets(self):
      frame = ttk.Frame(self.root, padding="10 10 10 10")
      frame.pack(fill=tk.BOTH, expand=True)

      frame.grid_rowconfigure(5, weight=1)
      frame.grid_columnconfigure(1, weight=1)

      self.directory_label = ttk.Label(frame, text="目录:")
      self.directory_label.grid(row=0, column=0, sticky=tk.W, pady=(20, 5))

      self.directory_entry = ttk.Entry(frame, width=50)
      self.directory_entry.grid(row=0, column=1, pady=(20, 5), padx=(0, 10), sticky=tk.EW)

      self.select_directory_button = ttk.Button(frame, text="选择目录", command=self.select_directory)
      self.select_directory_button.grid(row=0, column=2, pady=(20, 5))

      self.old_str_label = ttk.Label(frame, text="旧字符串:")
      self.old_str_label.grid(row=1, column=0, sticky=tk.W, pady=5)

      self.old_str_entry = ttk.Entry(frame, width=50)
      self.old_str_entry.grid(row=1, column=1, pady=5, padx=(0, 10), sticky=tk.EW)

      self.new_str_label = ttk.Label(frame, text="新字符串:")
      self.new_str_label.grid(row=2, column=0, sticky=tk.W, pady=5)

      self.new_str_entry = ttk.Entry(frame, width=50)
      self.new_str_entry.grid(row=2, column=1, pady=5, padx=(0, 10), sticky=tk.EW)

      self.regex_label = ttk.Label(frame, text="使用正则表达式:")
      self.regex_label.grid(row=3, column=0, sticky=tk.W, pady=5)

      self.use_regex_var = tk.StringVar()
      self.use_regex_combobox = ttk.Combobox(frame, textvariable=self.use_regex_var, state="readonly", values=["是", "否"])
      self.use_regex_combobox.current(1)
      self.use_regex_combobox.grid(row=3, column=1, pady=5, padx=(0, 10), sticky=tk.W)

      self.create_backup_var = tk.BooleanVar(value=True)
      self.create_backup_checkbox = ttk.Checkbutton(frame, text="创建备份", variable=self.create_backup_var)
      self.create_backup_checkbox.grid(row=4, column=0, sticky=tk.W, pady=5)

      self.log_text = ScrolledText(frame, height=10, width=70, wrap=tk.WORD, font=('Segoe UI', 10))
      self.log_text.grid(row=5, column=0, columnspan=3, pady=5, padx=(0, 10), sticky=tk.NSEW)

      self.progress_bar = ttk.Progressbar(frame, orient="horizontal", length=400, mode="determinate")
      self.progress_bar.grid(row=6, column=0, columnspan=3, pady=20, sticky=tk.EW)

      self.progress_label = ttk.Label(frame, text="")
      self.progress_label.grid(row=7, column=0, columnspan=3, pady=5)

      self.start_button = ttk.Button(frame, text="开始替换", command=self.start_replacing)
      self.start_button.grid(row=8, column=0, columnspan=3, pady=20)

    def select_directory(self):
      directory = filedialog.askdirectory()
      if directory:
            self.directory_entry.delete(0, tk.END)
            self.directory_entry.insert(0, directory)

    def start_replacing(self):
      directory = self.directory_entry.get()
      old_str = self.old_str_entry.get()
      new_str = self.new_str_entry.get()
      use_regex = self.use_regex_var.get() == "是"
      create_backup = self.create_backup_var.get()

      # 校验输入
      if not directory:
            messagebox.showwarning("错误", "请选择一个目录.")
            return
      if not old_str:
            messagebox.showwarning("错误", "请输入旧字符串.")
            return
      if not new_str:
            messagebox.showwarning("错误", "请输入新字符串.")
            return

      self.log_text.delete(1.0, tk.END)
      self.progress_bar["value"] = 0

      files_to_replace = []
      for root, dirs, files in os.walk(directory):
            for file in files:
                if file.endswith(('.txt', '.docx', '.xlsx')):
                  files_to_replace.append(os.path.join(root, file))

      if not files_to_replace:
            messagebox.showinfo("信息", "没有找到任何文件。")
            return

      self.progress_bar["maximum"] = len(files_to_replace)

      total_replacements = 0# 统计总的替换次数
      for index, file_path in enumerate(files_to_replace):
            replacements = self.replace_in_file(file_path, old_str, new_str, use_regex, create_backup)
            total_replacements += replacements# 累加每个文件的替换次数
            self.progress_bar["value"] = index + 1
            self.progress_label.config(text=f"进度: {index + 1}/{len(files_to_replace)}")
            self.root.update_idletasks()

      messagebox.showinfo("完成", f"所有文件处理完毕! 共进行了 {total_replacements} 次替换。")

    def replace_in_file(self, file_path, old_str, new_str, use_regex, create_backup):
      try:
            replacements = 0# 每个文件的替换次数
            if file_path.endswith('.txt'):
                replacements = self.replace_in_text_file(file_path, old_str, new_str, use_regex, create_backup)
            elif file_path.endswith('.docx'):
                replacements = self.replace_in_docx_file(file_path, old_str, new_str, use_regex, create_backup)
            elif file_path.endswith('.xlsx'):
                replacements = self.replace_in_xlsx_file(file_path, old_str, new_str, use_regex, create_backup)

            self.log_text.insert(tk.END, f"成功替换: {file_path},替换次数: {replacements}\n")
            return replacements
      except Exception as e:
            self.log_text.insert(tk.END, f"替换失败: {file_path} - {str(e)}\n")
            return 0# 失败则替换次数为 0

    def replace_in_text_file(self, file_path, old_str, new_str, use_regex, create_backup):
      with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

      if use_regex:
            new_content, count = re.subn(old_str, new_str, content, flags=re.MULTILINE)
            replacements = count
      else:
            new_content = content.replace(old_str, new_str)
            replacements = content.count(old_str)

      if create_backup:
            backup_file_path = file_path + '.bak'
            with open(backup_file_path, 'w', encoding='utf-8') as backup_file:
                backup_file.write(content)

      with open(file_path, 'w', encoding='utf-8') as file:
            file.write(new_content)

      return replacements

    def replace_in_docx_file(self, file_path, old_str, new_str, use_regex, create_backup):
      doc = Document(file_path)
      replacements = 0# 记录文档中的替换次数

      for para in doc.paragraphs:
            if use_regex:
                para.text, count = re.subn(old_str, new_str, para.text, flags=re.MULTILINE)
                replacements += count
            else:
                original_text = para.text
                para.text = para.text.replace(old_str, new_str)
                replacements += original_text.count(old_str)

      for table in doc.tables:
            for row in table.rows:
                for cell in row.cells:
                  if use_regex:
                        cell.text, count = re.subn(old_str, new_str, cell.text, flags=re.MULTILINE)
                        replacements += count
                  else:
                        original_text = cell.text
                        cell.text = cell.text.replace(old_str, new_str)
                        replacements += original_text.count(old_str)

      if create_backup:
            backup_file_path = file_path + '.bak'
            doc.save(backup_file_path)

      doc.save(file_path)

      return replacements

    def replace_in_xlsx_file(self, file_path, old_str, new_str, use_regex, create_backup):
      workbook = load_workbook(file_path)
      replacements = 0# 记录工作簿中的替换次数

      for sheet in workbook.worksheets:
            for row in sheet.iter_rows():
                for cell in row:
                  if cell.value and isinstance(cell.value, str):
                        if use_regex:
                            cell.value, count = re.subn(old_str, new_str, cell.value, flags=re.MULTILINE)
                            replacements += count
                        else:
                            original_value = cell.value
                            cell.value = cell.value.replace(old_str, new_str)
                            replacements += original_value.count(old_str)

      if create_backup:
            backup_file_path = file_path + '.bak'
            workbook.save(backup_file_path)

      workbook.save(file_path)

      return replacements

if __name__ == "__main__":
    root = tk.Tk()
    app = BatchReplaceApp(root)
    root.mainloop()```

Hamon 发表于 2024-5-29 20:46

加大难度,可以整个 office 文档处理的

老街518 发表于 2024-9-11 10:37

个Word的第一行,我有几千个Word,我需要批量把这几千个Word中的第一行,也就是标题替换成我需要的标题,需要替换的标题我整理到了一个txt里面,也可以放到Excel里面,然后进行批量替换这几千个Word。

比如我有3000个Word,需要把这3000个Word中的第一行也就是标题,每个Word的第一行就是标题,替换成我需要的标题。

然后我先整理标题列表txt,我把3000个需要替换的标题放到这个txt中

请问如何替换呢,比如3000个Word和1.txt列表我放到文件夹 【Word】中,我需要如何做呢,这个软件能加个这个功能不,大神?

宝宝丷 发表于 2024-5-29 16:59

感觉很乱 ,不太会排版{:1_937:}

奈酱 发表于 2024-5-29 17:18

可以字与字之间批量加空格

lvtaode0657 发表于 2024-5-29 19:03

很不错哦。辛苦了

dookp 发表于 2024-5-29 20:13

实用,感谢分享

afti 发表于 2024-5-29 21:11

看看文本替换效果如何

mubenxinsheng 发表于 2024-5-29 21:30

感谢分享{:1_893:}

爱飞的猫 发表于 2024-5-30 08:49

宝宝丷 发表于 2024-5-29 16:59
感觉很乱 ,不太会排版

图文混排可以参考:

- (https://www.52pojie.cn/thread-717627-1-1.html)
- [【公告】发帖代码插入以及添加链接教程(有福利)](https://www.52pojie.cn/thread-713042-1-1.html)

推荐使用 Markdown 插件,可以在本地排版好然后提交。

xiao_1245 发表于 2024-6-4 21:33

感谢分享,我辈楷模
页: [1] 2
查看完整版本: 批量替换文本文件 新增对 office 文档处理