29

我必须将Excel 工作表中的算法移植到 python 代码中,但我必须对Excel 文件中的算法进行逆向工程

Excel 工作表相当复杂,它包含许多单元格,其中有引用其他单元格的公式(也可以包含公式或常数)。

我的想法是使用 python 脚本来分析在单元格之间建立一种依赖关系表的工作表,即:

A1 取决于 B4,C5,E7 公式:“=sqrt(B4)+C5*E7”
A2 取决于 B5,C6 公式:“=sin(B5)*C6”
...

xlrd python 模块允许读取XLS 工作簿,但目前我可以访问单元格的,而不是公式

例如,使用以下代码,我可以简单地获取单元格的值:

import xlrd

#open the .xls file
xlsname="test.xls"
book = xlrd.open_workbook(xlsname)

#build a dictionary of the names->sheets of the book
sd={}
for s in book.sheets():
    sd[s.name]=s

#obtain Sheet "Foglio 1" from sheet names dictionary
sheet=sd["Foglio 1"]

#print value of the cell J141
print sheet.cell(142,9)

无论如何,似乎没有办法从.cell(...)方法返回的 Cell 对象中获取公式。在文档中,他们说可以获得公式的字符串版本(英文,因为没有关于存储在 Excel 文件中的函数名称翻译的信息)。他们谈论NameOperand类中的公式(表达式),无论如何我无法理解如何通过必须包含它们的Cell类实例来获取这些类的实例。

你能推荐一个从单元格中获取公式文本的代码片段吗?

4

6 回答 6

25

[免责声明]:我是xlrd.

公式文本的文档引用是关于“名称”公式的;阅读文档开头附近的“命名引用、常量、公式和宏”部分。这些公式在工作表范围或书籍范围内与名称相关联;它们与单个细胞无关。示例:PI映射到=22/7SALES映射到=Mktng!$A$2:$Z$99。编写名称公式反编译器是为了支持检查已定义名称的更简单和/或常见用法。

公式通常有以下几种:单元格、共享和数组(都与单元格直接或间接关联)、名称、数据验证和条件格式。

将一般公式从字节码反编译为文本是一个“正在进行中的工作”,慢慢地。请注意,假设它可用,您将需要解析文本公式以提取单元格引用。正确解析 Excel 公式并非易事;与 HTML 一样,使用正则表达式看起来很简单,但不起作用。最好直接从公式字节码中提取引用。

另请注意,基于单元格的公式可以引用名称,名称公式可以引用单元格和其他名称。因此,有必要从基于单元格的公式和名称公式中提取单元格和名称引用。获取有关可用共享公式的信息可能对您有用;否则解析以下内容:

B2 =A2
B3 =A3+B2
B4 =A4+B3
B5 =A5+B4
...
B60 =A60+B59

您需要自己推断B3:B60公式之间的相似性。

无论如何,上述任何一项都不太可能在短期内提供——xlrd优先事项在别处。

于 2011-01-14T20:16:46.673 回答
14

更新:我已经实现了一个小库来完全按照您的描述进行操作:从 Excel 电子表格中提取单元格和依赖项并将它们转换为 python 代码。代码在 github 上,欢迎打补丁 :)


只是要补充一点,您始终可以使用win32com与 excel 进行交互(不是很快,但它可以工作)。这确实允许您获得公式。一个教程可以在这里找到 【缓存副本】及详情可查在这一章当中 [缓存副本]

本质上,您只需执行以下操作:

app.ActiveWorkbook.ActiveSheet.Cells(r,c).Formula

至于建立一个单元格依赖关系表,一个棘手的事情是解析 excel 表达式。如果我没记错的话,你提到的 Trace 代码并不总是正确地做到这一点。我见过的最好的是EW Bachtal 的算法,其中一个 python 实现可用,效果很好。

于 2011-06-29T09:48:11.250 回答
7

所以我知道这是一篇非常古老的帖子,但我找到了一种从工作簿中的所有工作表中获取公式以及让新创建的工作簿保留所有格式的好方法。

第一步是将 .xlsx 文件的副本另存为 .xls - 在下面的代码中使用 .xls 作为文件名

使用 Python 2.7

from lxml import etree
from StringIO import StringIO
import xlsxwriter
import subprocess
from xlrd import open_workbook
from xlutils.copy import copy
from xlsxwriter.utility import xl_cell_to_rowcol
import os



file_name = '<YOUR-FILE-HERE>'
dir_path = os.path.dirname(os.path.realpath(file_name))

subprocess.call(["unzip",str(file_name+"x"),"-d","file_xml"])


xml_sheet_names = dict()

with open_workbook(file_name,formatting_info=True) as rb:
    wb = copy(rb)
    workbook_names_list = rb.sheet_names()
    for i,name in enumerate(workbook_names_list):
        xml_sheet_names[name] = "sheet"+str(i+1)

sheet_formulas = dict()
for i, k in enumerate(workbook_names_list):
    xmlFile = os.path.join(dir_path,"file_xml/xl/worksheets/{}.xml".format(xml_sheet_names[k]))
    with open(xmlFile) as f:
        xml = f.read()

    tree = etree.parse(StringIO(xml))
    context = etree.iterparse(StringIO(xml))

    sheet_formulas[k] = dict()
    for _, elem in context:
        if elem.tag.split("}")[1]=='f':
            cell_key = elem.getparent().get(key="r")
            cell_formula = elem.text
            sheet_formulas[k][cell_key] = str("="+cell_formula)

sheet_formulas

字典'sheet_formulas'的结构

{'Worksheet_Name': {'A1_cell_reference':'cell_formula'}}

示例结果:

{u'CY16': {'A1': '=Data!B5',
  'B1': '=Data!B1',
  'B10': '=IFERROR(Data!B12,"")',
  'B11': '=IFERROR(SUM(B9:B10),"")',
于 2016-11-13T08:31:30.797 回答
2

现在似乎不可能用 xlrd 做你想做的事。您可以查看这篇文章,详细说明为什么很难实现您需要的功能。

请注意,开发团队在 python-excel google 小组的支持方面做得很好。

于 2011-01-14T13:47:00.167 回答
0

耶!使用 win32com 它对我有用。

import    win32com.client
Excel = win32com.client.Dispatch("Excel.Application")

# python -m pip install pywin32
file=r'path Excel file'
wb = Excel.Workbooks.Open(file)
sheet = wb.ActiveSheet

#Get value
val = sheet.Cells(1,1).value
# Get Formula
sheet.Cells(6,2).Formula
于 2019-11-28T04:44:37.937 回答
-1

我知道这篇文章有点晚了,但这里没有提到一个建议。从工作表中剪切所有条目并使用特殊粘贴 (OpenOffice) 进行粘贴。这会将公式转换为数字,因此无需额外的编程,这对于小型工作簿来说是一个合理的解决方案。

于 2013-07-27T13:10:14.367 回答