DeepSeek辅助编写的将xlsx格式文件中sheet1.xml按需分别保留或去掉标签的程序

上文提到,xlsx格式文件中有一些标签属性可选,为了考察不同标签存在时,对程序读取解析性能的影响,需要一个程序来对已有的文件做加工。

交给DeepSeek的提示词如下

wps生成的xlsx中sheet1.xml 标签中有spans="1:cols"标签和r="n"标签(其中cols是总列数,n是数字行号),标签中有r="Bn"其中B是xlsx表格字母列号,n是数字行号)等标签,编写python程序,将sheet.xml中的上述标签分别保留或去掉,形成:

0.无span,无row的r,无col的r

1.有span,无row的r,无col的r

2.有span,有row的r,无col的r

分别输出,保存在子目录0、1、2的同名文件中
例如原始的xml,27340.884122563463474,第0种的要求27340.884122563463474也就是,第1种是27340.884122563463474,第2种是27340.884122563463474
请增加功能,把位于0、1、2目录的sheet1.xml文件替换到xlsx文件解压后的xl/worksheets目录下,xlsx文件中其他目录和文件保持不变,重新压缩生成新的xlsx文件,分别命名为wps0.xlsx,wps1.xlsx,wps2.xlsx。

经过两次修正,得到如下程序

python 复制代码
import os
import zipfile
import shutil
import xml.etree.ElementTree as ET
from pathlib import Path

def process_sheet_xml(input_xlsx_path, output_base_dir):
    """
    处理xlsx文件中的sheet1.xml,生成3种不同标签组合的版本,并重新打包为xlsx文件
    
    Args:
        input_xlsx_path: 输入的xlsx文件路径
        output_base_dir: 输出文件的基础目录
    """
    
    # 创建输出目录
    for i in range(3):
        os.makedirs(os.path.join(output_base_dir, str(i)), exist_ok=True)
    
    # 临时解压目录
    temp_dir = "temp_extract"
    os.makedirs(temp_dir, exist_ok=True)
    
    try:
        # 解压xlsx文件
        with zipfile.ZipFile(input_xlsx_path, 'r') as zip_ref:
            zip_ref.extractall(temp_dir)
        
        # 查找sheet1.xml文件
        sheet_path = None
        for root, dirs, files in os.walk(temp_dir):
            for file in files:
                if file == "sheet1.xml":
                    sheet_path = os.path.join(root, file)
                    break
            if sheet_path:
                break
        
        if not sheet_path:
            print("未找到sheet1.xml文件")
            return
        
        # 读取原始XML内容
        with open(sheet_path, 'r', encoding='utf-8') as f:
            original_xml = f.read()
        
        # 解析XML文件
        tree = ET.parse(sheet_path)
        root = tree.getroot()
        
        # 处理命名空间
        namespaces = {'ss': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
        ET.register_namespace('', namespaces['ss'])
        
        # 版本0: 无span, 无row的r, 无col的r
        version0 = ET.ElementTree(ET.fromstring(original_xml))
        for row in version0.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}row'):
            # 移除spans属性
            if 'spans' in row.attrib:
                del row.attrib['spans']
            # 移除r属性
            if 'r' in row.attrib:
                del row.attrib['r']
            # 移除c元素的r属性
            for cell in row.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c'):
                if 'r' in cell.attrib:
                    del cell.attrib['r']
        
        # 版本1: 有span, 无row的r, 无col的r
        version1 = ET.ElementTree(ET.fromstring(original_xml))
        for row in version1.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}row'):
            # 保留spans属性
            # 移除r属性
            if 'r' in row.attrib:
                del row.attrib['r']
            # 移除c元素的r属性
            for cell in row.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c'):
                if 'r' in cell.attrib:
                    del cell.attrib['r']
        
        # 版本2: 有span, 有row的r, 无col的r
        version2 = ET.ElementTree(ET.fromstring(original_xml))
        for row in version2.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}row'):
            # 保留spans属性
            # 保留r属性
            # 只移除c元素的r属性
            for cell in row.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}c'):
                if 'r' in cell.attrib:
                    del cell.attrib['r']
        
        # 保存三个版本的sheet1.xml到输出目录
        output_files = [
            os.path.join(output_base_dir, "0", "sheet1.xml"),
            os.path.join(output_base_dir, "1", "sheet1.xml"),
            os.path.join(output_base_dir, "2", "sheet1.xml")
        ]
        
        versions = [version0, version1, version2]
        
        for i, (version, output_file) in enumerate(zip(versions, output_files)):
            # 美化XML格式
            #indent(version.getroot())
            
            # 写入文件
            version.write(output_file, encoding='utf-8', xml_declaration=True)
            print(f"版本 {i} 的sheet1.xml已保存到: {output_file}")
        
        # 为每个版本创建新的xlsx文件
        create_new_xlsx_files(temp_dir, output_base_dir)
            
    finally:
        # 清理临时文件
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)

def create_new_xlsx_files(temp_dir, output_base_dir):
    """
    为每个版本创建新的xlsx文件
    """
    for version in range(3):
        # 创建版本特定的临时目录
        version_temp_dir = os.path.join(output_base_dir, f"temp_version_{version}")
        if os.path.exists(version_temp_dir):
            shutil.rmtree(version_temp_dir)
        shutil.copytree(temp_dir, version_temp_dir)
        
        # 替换sheet1.xml文件
        modified_sheet_path = os.path.join(output_base_dir, str(version), "sheet1.xml")
        target_sheet_path = find_sheet1_in_dir(version_temp_dir)
        
        if target_sheet_path and os.path.exists(modified_sheet_path):
            # 备份原始文件
            #backup_path = target_sheet_path + ".backup"
            #shutil.copy2(target_sheet_path, backup_path)
            
            # 替换为修改后的文件
            shutil.copy2(modified_sheet_path, target_sheet_path)
            print(f"版本 {version}: 已替换 {target_sheet_path}")
        
        # 创建新的xlsx文件
        output_xlsx = os.path.join(output_base_dir, f"wps{version}.xlsx")
        create_zip_from_directory(version_temp_dir, output_xlsx)
        print(f"版本 {version}: 已创建 {output_xlsx}")
        
        # 清理临时目录
        shutil.rmtree(version_temp_dir)

def find_sheet1_in_dir(directory):
    """
    在目录中查找sheet1.xml文件
    """
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file == "sheet1.xml":
                return os.path.join(root, file)
    return None

def create_zip_from_directory(directory, output_zip):
    """
    将目录内容压缩为xlsx文件
    """
    # 确保输出文件不存在
    if os.path.exists(output_zip):
        os.remove(output_zip)
    
    # 创建zip文件
    with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zipf: # , compresslevel=6
        for root, dirs, files in os.walk(directory):
            for file in files:
                file_path = os.path.join(root, file)
                # 计算在zip中的相对路径
                arcname = os.path.relpath(file_path, directory)
                zipf.write(file_path, arcname)

def indent(elem, level=0):
    """
    美化XML格式,添加缩进
    """
    i = "\n" + level * "  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for child in elem:
            indent(child, level + 1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

def main():
    # 使用示例
    input_file = "4/wps_h0.xlsx"  # 替换为你的xlsx文件路径
    output_dir = "4"      # 输出目录
    
    # 确保输出目录存在
    os.makedirs(output_dir, exist_ok=True)
    
    process_sheet_xml(input_file, output_dir)
    print("处理完成!")
    print(f"生成的文件:")
    print(f"  wps0.xlsx - 无span, 无row的r, 无col的r")
    print(f"  wps1.xlsx - 有span, 无row的r, 无col的r")  
    print(f"  wps2.xlsx - 有span, 有row的r, 无col的r")
    print(f"文件保存在: {os.path.abspath(output_dir)}")

if __name__ == "__main__":
    main()

执行情况如下

bash 复制代码
python3 stripxml3.py 4/wps_h0.xlsx 4
版本 0 的sheet1.xml已保存到: 4/0/sheet1.xml
版本 1 的sheet1.xml已保存到: 4/1/sheet1.xml
版本 2 的sheet1.xml已保存到: 4/2/sheet1.xml
版本 0: 已替换 4/temp_version_0/xl/worksheets/sheet1.xml
版本 0: 已创建 4/wps0.xlsx
版本 1: 已替换 4/temp_version_1/xl/worksheets/sheet1.xml
版本 1: 已创建 4/wps1.xlsx
版本 2: 已替换 4/temp_version_2/xl/worksheets/sheet1.xml
版本 2: 已创建 4/wps2.xlsx
处理完成!
生成的文件:
  wps0.xlsx - 无span, 无row的r, 无col的r
  wps1.xlsx - 有span, 无row的r, 无col的r
  wps2.xlsx - 有span, 有row的r, 无col的r
文件保存在: /par/4

ls -l 4/wps*xlsx
-rw-r--r-- 1 root root 6839896 Aug 21 11:10 4/wps0.xlsx
-rw-r--r-- 1 root root 6839896 Aug 21 11:10 4/wps1.xlsx
-rw-r--r-- 1 root root 6854333 Aug 21 11:10 4/wps2.xlsx
-rw-r--r-- 1 root root 9315700 Aug 21 10:28 4/wps_h0.xlsx

程序功能正确,但处理大文件太耗内存,我处理一个4万行256列的数据,内存不足,使用了交换文件,还是无法完成。于是继续提示:

请把它改成用正则表达式替换,只处理和标签左侧部分,改写后写入xml, 其余的标签按原文件复制,用一个64KB缓冲区边读边写,不全部存在内存。

他给出的程序如下

python 复制代码
import os
import re
import zipfile
import shutil
from pathlib import Path

def process_sheet_xml(input_xlsx_path, output_base_dir):
    """
    使用正则表达式和流式处理处理xlsx文件中的sheet1.xml
    """
    
    # 创建输出目录
    for i in range(3):
        os.makedirs(os.path.join(output_base_dir, str(i)), exist_ok=True)
    
    # 临时解压目录
    temp_dir = "temp_extract"
    os.makedirs(temp_dir, exist_ok=True)
    
    try:
        # 解压xlsx文件
        with zipfile.ZipFile(input_xlsx_path, 'r') as zip_ref:
            zip_ref.extractall(temp_dir)
        
        # 查找sheet1.xml文件
        sheet_path = find_sheet1_in_dir(temp_dir)
        if not sheet_path:
            print("未找到sheet1.xml文件")
            return
        
        # 使用正则表达式处理三个版本
        process_with_regex(sheet_path, output_base_dir)
        
        # 为每个版本创建新的xlsx文件
        create_new_xlsx_files(temp_dir, output_base_dir)
            
    finally:
        # 清理临时文件
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)

def process_with_regex(sheet_path, output_base_dir):
    """
    使用正则表达式处理sheet1.xml文件
    """
    # 定义正则表达式模式
    row_pattern = re.compile(r'<row\s+([^>]*)>')
    cell_pattern = re.compile(r'<c\s+([^>]*)>')
    
    # 输出文件路径
    output_files = [
        os.path.join(output_base_dir, "0", "sheet1.xml"),
        os.path.join(output_base_dir, "1", "sheet1.xml"),
        os.path.join(output_base_dir, "2", "sheet1.xml")
    ]
    
    # 缓冲区大小
    buffer_size = 65536  # 64KB
    
    # 处理三个版本
    for version, output_file in enumerate(output_files):
        process_single_version(sheet_path, output_file, version, row_pattern, cell_pattern, buffer_size)
        print(f"版本 {version} 已处理完成: {output_file}")

def process_single_version(input_path, output_path, version, row_pattern, cell_pattern, buffer_size):
    """
    处理单个版本的sheet1.xml
    """
    with open(input_path, 'r', encoding='utf-8') as infile, \
         open(output_path, 'w', encoding='utf-8') as outfile:
        
        buffer = []
        while True:
            # 读取一块数据
            chunk = infile.read(buffer_size)
            if not chunk:
                break
            
            buffer.append(chunk)
            current_text = ''.join(buffer)
            
            # 处理当前文本块
            processed_text = process_text_chunk(current_text, version, row_pattern, cell_pattern)
            
            # 写入处理后的文本
            outfile.write(processed_text)
            
            # 清空缓冲区,保留最后部分以避免截断标签
            buffer = [current_text[-100:]]  # 保留最后100字符避免标签截断
        
        # 处理最后剩余的部分
        if buffer:
            final_text = ''.join(buffer)
            processed_final = process_text_chunk(final_text, version, row_pattern, cell_pattern)
            outfile.write(processed_final)

def process_text_chunk(text, version, row_pattern, cell_pattern):
    """
    处理文本块中的row和cell标签
    """
    # 处理row标签
    def process_row(match):
        attrs = match.group(1)
        if version == 0:
            # 版本0: 移除所有属性
            return '<row>'
        elif version == 1:
            # 版本1: 只保留spans属性
            spans_match = re.search(r'spans="[^"]*"', attrs)
            if spans_match:
                return f'<row {spans_match.group(0)}>'
            else:
                return '<row>'
        else:  # version == 2
            # 版本2: 保留spans和r属性
            spans_match = re.search(r'spans="[^"]*"', attrs)
            r_match = re.search(r'r="\d+"', attrs)
            kept_attrs = []
            if spans_match:
                kept_attrs.append(spans_match.group(0))
            if r_match:
                kept_attrs.append(r_match.group(0))
            if kept_attrs:
                return f'<row {" ".join(kept_attrs)}>'
            else:
                return '<row>'
    
    # 处理cell标签
    def process_cell(match):
        # 所有版本都移除cell的r属性
        attrs = match.group(1)
        # 移除r属性
        attrs = re.sub(r'r="[^"]*"', '', attrs)
        attrs = attrs.strip()
        if attrs:
            return f'<c {attrs}>'
        else:
            return '<c>'
    
    # 应用替换
    text = row_pattern.sub(process_row, text)
    text = cell_pattern.sub(process_cell, text)
    
    return text

def create_new_xlsx_files(temp_dir, output_base_dir):
    """
    为每个版本创建新的xlsx文件
    """
    for version in range(3):
        # 创建版本特定的临时目录
        version_temp_dir = os.path.join(output_base_dir, f"temp_version_{version}")
        if os.path.exists(version_temp_dir):
            shutil.rmtree(version_temp_dir)
        shutil.copytree(temp_dir, version_temp_dir)
        
        # 替换sheet1.xml文件
        modified_sheet_path = os.path.join(output_base_dir, str(version), "sheet1.xml")
        target_sheet_path = find_sheet1_in_dir(version_temp_dir)
        
        if target_sheet_path and os.path.exists(modified_sheet_path):
            # 替换为修改后的文件
            shutil.copy2(modified_sheet_path, target_sheet_path)
            print(f"版本 {version}: 已替换 {target_sheet_path}")
        
        # 创建新的xlsx文件
        output_xlsx = os.path.join(output_base_dir, f"wps{version}.xlsx")
        create_zip_from_directory(version_temp_dir, output_xlsx)
        print(f"版本 {version}: 已创建 {output_xlsx}")
        
        # 清理临时目录
        shutil.rmtree(version_temp_dir)

def find_sheet1_in_dir(directory):
    """
    在目录中查找sheet1.xml文件
    """
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file == "sheet1.xml":
                return os.path.join(root, file)
    return None

def create_zip_from_directory(directory, output_zip):
    """
    将目录内容压缩为xlsx文件
    """
    # 确保输出文件不存在
    if os.path.exists(output_zip):
        os.remove(output_zip)
    
    # 创建zip文件
    with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for root, dirs, files in os.walk(directory):
            for file in files:
                file_path = os.path.join(root, file)
                # 计算在zip中的相对路径
                arcname = os.path.relpath(file_path, directory)
                zipf.write(file_path, arcname)

def main():
    # 使用示例
    input_file = "4/wps_h0.xlsx"  # 替换为你的xlsx文件路径
    output_dir = "4"      # 输出目录
    
    # 确保输出目录存在
    os.makedirs(output_dir, exist_ok=True)
    
    process_sheet_xml(input_file, output_dir)
    print("处理完成!")
    print(f"生成的文件:")
    print(f"  wps0.xlsx - 无span, 无row的r, 无col的r")
    print(f"  wps1.xlsx - 有span, 无row的r, 无col的r")  
    print(f"  wps2.xlsx - 有span, 有row的r, 无col的r")
    print(f"文件保存在: {os.path.abspath(output_dir)}")

if __name__ == "__main__":
    main()

这个正则版本不知为何处理小文件正确,大文件就格式错误,本来是大文件想用它,只好放弃。

这也说明,市面上的第三方处理xlsx程序为什么大都是读写分离的,比如calamine只读,libxlsxwrite只写,因为像Excel和WPS那样读写兼顾真的要复杂很多。

相关推荐
HenryLin41 分钟前
美股量化分析系统 - 模块调用流程文档
python
跟橙姐学代码43 分钟前
一文读懂 Python 的 JSON 模块:从零到高手的进阶之路
前端·python
躺不平的小刘1 小时前
从YOLOv5到RKNN:零冲突转换YOLOv5模型至RK3588 NPU全指南
linux·python·嵌入式硬件·yolo·conda·pyqt·pip
文火冰糖的硅基工坊1 小时前
[激光原理与应用-317]:光学设计 - Solidworks - 草图
开发语言·python·信息可视化·系统架构
高级测试工程师欧阳2 小时前
python中selenium怎么使用
python·pandas
BertieHuang2 小时前
(一)深入源码,从 0 到 1 实现 Cursor
人工智能·python·程序员
以泪为证2 小时前
WebSocket 任务分发系统代码深度分析与应用
python
jumin18063 小时前
python采用jdbc连接kerberos认证的hive
python·apache hive
Ice__Cai3 小时前
Flask 路由详解:构建灵活的 URL 映射系统
开发语言·python·flask