Word文档内容批量替换脚本

word_replace_v1.py

# -*- coding: utf-8 -*-
"""
Word文档批量替换脚本

功能说明:
1. 递归处理当前目录及所有子目录中的Word文档(.docx格式)
2. 将文档中的指定文本替换为新文本,同时保留原有格式
3. 支持处理段落和表格中的文本

使用方法:
1. 基本使用:python word_replace.py --old "原文本" --new "新文本"
2. 指定目录:python word_replace.py --dir "/path/to/docs" --old "原文本" --new "新文本"
3. 查看帮助:python word_replace.py --help

注意事项:
- 需要安装python-docx库:pip install python-docx
- 仅支持.docx格式,不支持旧的.doc格式
- 会直接修改原文件,建议先备份重要文档
- 如果文本跨多个格式块(run),可能无法完全替换

示例:
python word_replace.py --old "公司A" --new "公司B"
python word_replace.py --dir "./项目文档" --old "2023年" --new "2024年"
"""

import os
import argparse
from docx import Document


def replace_in_docx(file_path, old_text, new_text):
    """
    在单个Word文档中替换文本,保留格式
    
    参数:
        file_path: Word文档路径
        old_text: 要替换的旧文本
        new_text: 替换后的新文本
    """
    doc = Document(file_path)
    
    # 替换段落中的文本
    for paragraph in doc.paragraphs:
        if old_text in paragraph.text:
            for run in paragraph.runs:
                if old_text in run.text:
                    run.text = run.text.replace(old_text, new_text)
    
    # 替换表格中的文本
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for paragraph in cell.paragraphs:
                    if old_text in paragraph.text:
                        for run in paragraph.runs:
                            if old_text in run.text:
                                run.text = run.text.replace(old_text, new_text)
    
    doc.save(file_path)
    print(f'已处理: {file_path}')


def process_all_word_files(directory, old_text, new_text):
    """
    递归处理所有Word文档
    
    参数:
        directory: 要处理的目录路径
        old_text: 要替换的旧文本
        new_text: 替换后的新文本
    """
    processed_count = 0
    error_count = 0
    
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.docx'):
                file_path = os.path.join(root, file)
                try:
                    replace_in_docx(file_path, old_text, new_text)
                    processed_count += 1
                except Exception as e:
                    error_count += 1
                    print(f'处理失败 {file_path}: {e}')
    
    print(f'\n处理完成!成功处理 {processed_count} 个文档,失败 {error_count} 个文档')


def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description='Word文档批量替换脚本 - 递归替换所有Word文档中的文本并保留格式',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
示例:
  %(prog)s --old "公司A" --new "公司B"
  %(prog)s --dir "./项目文档" --old "2023年" --new "2024年"
  %(prog)s --dir "." --old "旧版本" --new "新版本" --dry-run

注意:
  - 需要安装python-docx库: pip install python-docx
  - 仅支持.docx格式,不支持旧的.doc格式
  - 默认会直接修改原文件,使用--dry-run参数可预览而不实际修改
        '''
    )
    
    parser.add_argument('--dir', default='.', 
                       help='要处理的目录路径(默认当前目录)')
    parser.add_argument('--old', required=True, 
                       help='要替换的旧文本(必填)')
    parser.add_argument('--new', required=True, 
                       help='替换后的新文本(必填)')
    parser.add_argument('--dry-run', action='store_true',
                       help='预览模式,显示将要修改的文件但不实际执行替换')
    
    args = parser.parse_args()
    
    # 检查目录是否存在
    if not os.path.exists(args.dir):
        print(f"错误:目录 '{args.dir}' 不存在")
        return
    
    # 如果是预览模式,只显示将要处理的文件
    if args.dry_run:
        print("预览模式(不会实际修改文件):")
        docx_files = []
        for root, dirs, files in os.walk(args.dir):
            for file in files:
                if file.endswith('.docx'):
                    file_path = os.path.join(root, file)
                    docx_files.append(file_path)
                    print(f"  {file_path}")
        
        if docx_files:
            print(f"\n找到 {len(docx_files)} 个Word文档,将替换文本: '{args.old}' -> '{args.new}'")
        else:
            print("未找到任何Word文档(.docx)")
        return
    
    # 执行实际替换操作
    print(f"开始处理目录: {args.dir}")
    print(f"替换文本: '{args.old}' -> '{args.new}'")
    process_all_word_files(args.dir, args.old, args.new)


if __name__ == '__main__':
    main()

word_replace_v2.py

#!/usr/bin/env python3
"""
Word文档批量替换脚本 - 增强版

功能说明:
1. 递归处理当前目录及所有子目录中的Word文档(.docx格式)
2. 将文档中的指定文本替换为新文本,同时保留原有格式
3. 支持处理段落和表格中的文本
4. 增强功能:能够处理跨多个格式块(run)的文本替换

使用方法:
1. 基本使用:python word_replace.py --old "原文本" --new "新文本"
2. 指定目录:python word_replace.py --dir "/path/to/docs" --old "原文本" --new "新文本"
3. 查看帮助:python word_replace.py --help

注意事项:
- 需要安装python-docx库:pip install python-docx
- 仅支持.docx格式,不支持旧的.doc格式
- 会直接修改原文件,建议先备份重要文档
- 增强版能够处理跨多个格式块的文本替换

示例:
python word_replace.py --old "公司A" --new "公司B"
python word_replace.py --dir "./项目文档" --old "2023年" --new "2024年"
"""

import os
import argparse
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_COLOR_INDEX


def find_text_in_runs(paragraph, old_text):
    """
    在段落的所有run中查找文本,处理跨多个run的情况
    
    参数:
        paragraph: 段落对象
        old_text: 要查找的文本
        
    返回:
        包含匹配信息的字典,或None如果未找到
    """
    full_text = paragraph.text
    if old_text not in full_text:
        return None
    
    # 查找文本在完整段落中的位置
    start_idx = full_text.find(old_text)
    end_idx = start_idx + len(old_text)
    
    # 确定哪些run包含了目标文本
    run_start_idx = 0
    matching_runs = []
    
    for run in paragraph.runs:
        run_end_idx = run_start_idx + len(run.text)
        
        # 检查这个run是否包含目标文本的一部分
        if run_start_idx <= start_idx < run_end_idx or \
           run_start_idx < end_idx <= run_end_idx or \
           (start_idx <= run_start_idx and run_end_idx <= end_idx):
            matching_runs.append({
                'run': run,
                'run_start': run_start_idx,
                'run_end': run_end_idx,
                'text_start': max(start_idx, run_start_idx),
                'text_end': min(end_idx, run_end_idx)
            })
        
        run_start_idx = run_end_idx
    
    if not matching_runs:
        return None
    
    return {
        'matching_runs': matching_runs,
        'text_start': start_idx,
        'text_end': end_idx,
        'old_text': old_text
    }


def replace_text_in_paragraph(paragraph, old_text, new_text):
    """
    替换段落中的文本,处理跨多个run的情况
    
    参数:
        paragraph: 段落对象
        old_text: 要替换的旧文本
        new_text: 替换后的新文本
    """
    match_info = find_text_in_runs(paragraph, old_text)
    if not match_info:
        return False
    
    # 如果文本只在一个run中,直接替换
    if len(match_info['matching_runs']) == 1:
        run_info = match_info['matching_runs'][0]
        run = run_info['run']
        
        # 计算文本在run中的位置
        run_local_start = run_info['text_start'] - run_info['run_start']
        run_local_end = run_info['text_end'] - run_info['run_start']
        
        # 替换文本
        run.text = run.text[:run_local_start] + new_text + run.text[run_local_end:]
        return True
    
    # 处理跨多个run的文本替换
    # 策略:保留第一个run的格式,将替换后的文本放入第一个run,删除其他run中的相关内容
    
    # 获取第一个匹配的run
    first_run_info = match_info['matching_runs'][0]
    first_run = first_run_info['run']
    
    # 计算在第一个run中的文本位置
    first_run_local_start = first_run_info['text_start'] - first_run_info['run_start']
    
    # 构建新文本
    # 第一个run中目标文本之前的部分 + 新文本
    new_run_text = first_run.text[:first_run_local_start] + new_text
    
    # 更新第一个run的文本
    first_run.text = new_run_text
    
    # 处理后续的run:删除它们中包含的目标文本部分
    for i, run_info in enumerate(match_info['matching_runs']):
        if i == 0:  # 第一个run已经处理过
            continue
            
        run = run_info['run']
        run_local_start = run_info['text_start'] - run_info['run_start']
        run_local_end = run_info['text_end'] - run_info['run_start']
        
        # 如果这个run只包含目标文本的一部分,删除这部分
        if run_local_end < len(run.text):
            run.text = run.text[run_local_end:]
        else:
            # 如果整个run都是目标文本的一部分,清空它
            run.text = ""
    
    return True


def replace_in_docx(file_path, old_text, new_text):
    """
    在单个Word文档中替换文本,处理跨多个格式块的情况
    
    参数:
        file_path: Word文档路径
        old_text: 要替换的旧文本
        new_text: 替换后的新文本
    """
    doc = Document(file_path)
    replaced_count = 0
    
    # 替换段落中的文本
    for paragraph in doc.paragraphs:
        if old_text in paragraph.text:
            if replace_text_in_paragraph(paragraph, old_text, new_text):
                replaced_count += 1
    
    # 替换表格中的文本
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for paragraph in cell.paragraphs:
                    if old_text in paragraph.text:
                        if replace_text_in_paragraph(paragraph, old_text, new_text):
                            replaced_count += 1
    
    if replaced_count > 0:
        doc.save(file_path)
        print(f'已处理: {file_path} (替换了 {replaced_count} 处)')
    else:
        print(f'跳过: {file_path} (未找到匹配文本)')


def process_all_word_files(directory, old_text, new_text):
    """
    递归处理所有Word文档
    
    参数:
        directory: 要处理的目录路径
        old_text: 要替换的旧文本
        new_text: 替换后的新文本
    """
    processed_count = 0
    error_count = 0
    
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.docx'):
                file_path = os.path.join(root, file)
                try:
                    replace_in_docx(file_path, old_text, new_text)
                    processed_count += 1
                except Exception as e:
                    error_count += 1
                    print(f'处理失败 {file_path}: {e}')
    
    print(f'\n处理完成!成功处理 {processed_count} 个文档,失败 {error_count} 个文档')


def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description='Word文档批量替换脚本 - 增强版,支持跨格式块文本替换',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
示例:
  %(prog)s --old "公司A" --new "公司B"
  %(prog)s --dir "./项目文档" --old "2023年" --new "2024年"
  %(prog)s --dir "." --old "旧版本" --new "新版本" --dry-run

注意:
  - 需要安装python-docx库: pip install python-docx
  - 仅支持.docx格式,不支持旧的.doc格式
  - 默认会直接修改原文件,使用--dry-run参数可预览而不实际修改
  - 增强版能够处理跨多个格式块的文本替换
        '''
    )
    
    parser.add_argument('--dir', default='.', 
                       help='要处理的目录路径(默认当前目录)')
    parser.add_argument('--old', required=True, 
                       help='要替换的旧文本(必填)')
    parser.add_argument('--new', required=True, 
                       help='替换后的新文本(必填)')
    parser.add_argument('--dry-run', action='store_true',
                       help='预览模式,显示将要修改的文件但不实际执行替换')
    
    args = parser.parse_args()
    
    # 检查目录是否存在
    if not os.path.exists(args.dir):
        print(f"错误:目录 '{args.dir}' 不存在")
        return
    
    # 如果是预览模式,只显示将要处理的文件
    if args.dry_run:
        print("预览模式(不会实际修改文件):")
        docx_files = []
        for root, dirs, files in os.walk(args.dir):
            for file in files:
                if file.endswith('.docx'):
                    file_path = os.path.join(root, file)
                    docx_files.append(file_path)
                    print(f"  {file_path}")
        
        if docx_files:
            print(f"\n找到 {len(docx_files)} 个Word文档,将替换文本: '{args.old}' -> '{args.new}'")
        else:
            print("未找到任何Word文档(.docx)")
        return
    
    # 执行实际替换操作
    print(f"开始处理目录: {args.dir}")
    print(f"替换文本: '{args.old}' -> '{args.new}'")
    print("增强模式:支持跨多个格式块的文本替换")
    process_all_word_files(args.dir, args.old, args.new)


if __name__ == '__main__':
    main()
posted @ 2025-09-23 11:26  wanghongwei-dev  阅读(58)  评论(0)    收藏  举报