You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python的ezodf库更新ODS文件计数器失效问题排查

问题:ODS文件计数器更新后未保存生效

背景说明

  • 有两个ODS文件:
    1. AppAndroid.ods:包含appFromAppStore和Others两个工作表,表头为Application、No.、Version、Category、Encryption、Interest、Comments、Counter,用于记录历史应用信息,Counter列统计应用被识别次数。
    2. UnsupportedApp.ods:包含appFromAppStore_Result和Others_Result两个工作表,表头无Counter列,是临时文件,每次Python程序执行时更新。
  • 程序逻辑:将UnsupportedApp.ods的信息同步到AppAndroid.ods,若应用已存在则Counter加1,不存在则新增行并设Counter=1。
  • 问题现象:控制台显示计数器正常递增,但保存后打开ODS文件数据未更新。

原代码

import os
import pyexcel as pe
import ezodf

def increment_counter(app_name, data):
    app_exists = any(row['Application'] == app_name for row in data)

    if app_exists:
        for row in data:
            if row['Application'] == app_name:
                row['Counter'] += 1
    else:
        new_row = {'Application': app_name, 'Version': '', 'Category': '',
                   'Encryption': '', 'Interest': '', 'Comments': '', 'Counter': 1}
        data.append(new_row)


def update_counter(source_file, destination_file, source_sheet, destination_sheet):
    source_data = pe.get_records(file_name=source_file, sheet_name=source_sheet)
    destination_data = pe.get_records(file_name=destination_file, sheet_name=destination_sheet)

    for row in source_data:
        app_name = row['Application']
        increment_counter(app_name, destination_data)

    destination_book = ezodf.opendoc(destination_file)

    destination_sheet_index = -1
    for i, sheet in enumerate(destination_book.sheets):
        if sheet.name == destination_sheet:
            destination_sheet_index = i
            break

    destination_book.sheets[destination_sheet_index].data = destination_data

    destination_book.saveas(destination_file)
    bak_file = f"{destination_file}.bak"
    if os.path.exists(bak_file):
        os.remove(bak_file)


source_file = 'UnsupportedApp.ods'
source_sheet = 'appFromAppStore_Result'
destination_sheet = 'appFromAppStore'
update_counter(source_file, filePathAppAndroid, source_sheet, destination_sheet)

核心解决思路

1. 修复数据格式不兼容问题(根本原因)

pyexcel.get_records()返回字典列表,但ezodf工作表的data属性要求的是二维列表(列表的列表),直接赋值字典列表会导致文件无法识别更新内容。修正步骤:

  • 先获取目标工作表的表头顺序,确保数据列对应正确;
  • 将更新后的字典列表按表头顺序转换为二维列表;
  • 清空原有数据行(保留表头),再逐行插入新数据。

2. 完善新增行的字段赋值

原代码新增行时将Version等字段设为空,既不符合需求也可能导致数据结构异常,改为从源数据行中读取对应字段值。

3. 优化文件操作逻辑

  • 避免使用saveas()生成冗余备份文件,改用save()直接覆盖;
  • 增加文件占用检测,避免因目标文件被其他程序(如LibreOffice、WPS)打开导致保存失败;
  • 明确工作表定位逻辑,避免因索引错误导致更新无效。

修正后的代码

import os
import pyexcel as pe
import ezodf

def increment_counter(app_name, data, source_row):
    app_exists = any(row['Application'] == app_name for row in data)

    if app_exists:
        for row in data:
            if row['Application'] == app_name:
                row['Counter'] += 1
    else:
        new_row = {
            'Application': app_name,
            'Version': source_row.get('Version', ''),
            'Category': source_row.get('Category', ''),
            'Encryption': source_row.get('Encryption', ''),
            'Interest': source_row.get('Interest', ''),
            'Comments': source_row.get('Comments', ''),
            'Counter': 1
        }
        data.append(new_row)


def update_counter(source_file, destination_file, source_sheet, destination_sheet):
    source_data = pe.get_records(file_name=source_file, sheet_name=source_sheet)
    destination_data = pe.get_records(file_name=destination_file, sheet_name=destination_sheet)

    for row in source_data:
        app_name = row['Application']
        increment_counter(app_name, destination_data, row)

    destination_book = ezodf.opendoc(destination_file)

    # 定位目标工作表
    dest_sheet = None
    for sheet in destination_book.sheets:
        if sheet.name == destination_sheet:
            dest_sheet = sheet
            break
    if not dest_sheet:
        print(f"工作表 {destination_sheet} 不存在")
        return

    # 获取表头顺序,确保列对应正确
    headers = [cell.value for cell in dest_sheet.row(0)]
    # 将字典列表转换为二维列表
    new_data_rows = []
    for row_dict in destination_data:
        row_list = [row_dict.get(header, '') for header in headers]
        new_data_rows.append(row_list)

    # 清空原有数据行(保留表头)
    while len(dest_sheet.rows) > 1:
        dest_sheet.delete_row(1)
    # 插入新数据行
    for row_idx, row_data in enumerate(new_data_rows, start=1):
        dest_sheet.insert_row(row_idx)
        for col_idx, cell_value in enumerate(row_data):
            dest_sheet[row_idx, col_idx].set_value(cell_value)

    # 保存文件并处理异常
    try:
        destination_book.save()
        bak_file = f"{destination_file}.bak"
        if os.path.exists(bak_file):
            os.remove(bak_file)
        print("文件更新保存成功")
    except PermissionError:
        print("权限错误:文件可能被其他程序打开,请关闭后重试")


source_file = 'UnsupportedApp.ods'
source_sheet = 'appFromAppStore_Result'
destination_sheet = 'appFromAppStore'
filePathAppAndroid = 'AppAndroid.ods'  # 补充定义目标文件路径
update_counter(source_file, filePathAppAndroid, source_sheet, destination_sheet)

内容的提问来源于stack exchange,提问作者Louis Chabert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:35:06