C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#解析Excel公式

使用C#实现解析Excel公式

作者:葡萄城官网

在日常工作中,我们经常需要在Excel中使用公式对表中数据进行计算和分析,所以本文小编主要来和大家介绍一下如何在C#中实现解析Excel公式,感兴趣的可以了解下

前言

在日常工作中,我们经常需要在Excel中使用公式对表中数据进行计算(求和、求差和求均值等)和分析,从而实现对数据的分类,通常情况下,当数据量较少或场景变化单一的情况下,使用公式可以满足用户的要求,但当数据量较大或者场景变化复杂的情况下,使用公式也无法满足用户的需求的情况。这个时候就可以用编码的方式来解决,以下面的背景需求为例,小编将为大家介绍如何使用葡萄城公司基于 .NET 和 .NET Core 平台的服务端高性能表格组件组件GrapeCity Documents for Excel (以下简称GcExcel)解析Excel中的现有公式并根据需求对其进行修改。

背景需求

下图是一张销售数据表,左侧显示原始销售数据,包括销售代表的姓名、地区、产品和销售数量,右侧显示了从原始数据中提取的特定的销售代表对应的销售分析结果,以及每个产品区域组合的月度销售目标进度。目标进度的标准如下:

低于 2500:低于目标

超过 3000:达到目标

超过 5000:高于目标

一般情况下,我们使用Excel中的 IF、ISNUMBER 和 FILTER 函数就可以实现将左侧的销售原始数据转化为右侧的销售分析结果,如下所示:

=IF(ISNUMBER(FILTER(A2:D19,A2:A19="Fritz")),IFS(FILTER(A2:D19,A2:A19="Fritz")>5000,"Above Target",FILTER(A2:D19,A2:A19="Fritz")>3000,"On Target",FILTER(A2:D19,A2:A19="Fritz")<2500,"Below Target"),FILTER(A2:D19,A2:A19="Fritz"))

但是这样的话就会出现一个问题,对于不同的人名,小编需要将上面公式中销售代表的姓名进行替换,也就是需要不断地手动改变姓名执行操作,这一举动不仅枯燥,而且很容易出错。因此这个时候就可以使用GcExcel通过解析公式并使用解析的语法树轻松替换销售代表姓名,可以简化此任务。

使用 C# 解析和修改 Excel 公式

首先,创建一个新的 C#(.NET Core) 项目,并使用NuGet 包管理器安装 GcExcel 包,然后按照前面的步骤操作。

1、使用示例数据初始化工作簿

实例化 Workbook 类的实例并从 Excel 文件导入示例数据,如下所示。

//Create a new workbook
var workbook = new GrapeCity.Documents.Excel.Workbook();           
//Load sample data from excel file
workbook.Open("SampleData.xlsx");
//Enable dynamic array formula
workbook.AllowDynamicArray = true;

2、提取公式

在工作簿加载示例数据和预期公式后,我们从工作表中提取所需的公式,以便使用 Formula 属性进行解析和修改。

GcExcel API 提供的公式解析器希望传递的公式不带“=”(等于)运算符,以便成功进行公式解析。因此,请注意如何在不使用“=”运算符的情况下提取公式。

//Fetch worksheet
var worksheet = workbook.Worksheets[0];
//Fetch the original formula which needs to be parsed.
var originalFormula = worksheet.Range["H3"].Formula.Substring(1);

3、解析公式

调用 FormulaSynatxTree 类的 Parse 方法来解析公式并生成语法树,帮助您理解公式包含的所有不同类型的值、运算符和函数。

公式语法树的每个标记都由 GcExcel API 中的其他类表示,例如函数的 FunctionNode、运算符的 OperatorNode 等。

下面的代码解析了上一步中提取的销售分析公式。然后,它将生成的 FormulaSyntaxTree 中的值附加到工作簿,该工作簿随后保存为 Excel 文件,以帮助您了解公式的语法树。

//Method to parse a formula and print the syntax tree
public static void ParseAndPrint(IWorksheet worksheet, string formula)
{
   // Get syntax tree
   var syntaxTree = FormulaSyntaxTree.Parse(formula);

   // Flatten nodes
   var displayItems = new List<(string TypeName, int IndentLevel, string Content)>();

   void flatten(SyntaxNode node, int level)
   {
      displayItems.Add((node.GetType().Name, level, node.ToString()));
      foreach (var child in node.Children)
      {
         flatten(child, level + 1);
      }
   }

   flatten(syntaxTree.Root, 0);

   // Output          
   worksheet.ShowRowOutline = false;
   worksheet.OutlineColumn.ColumnIndex = 1;

   // Header
   worksheet.Range["A1"].Value = "Formula";
   worksheet.Range["A3"].Value = "Syntax node";
   worksheet.Range["B3"].Value = "Part";

   // Values
   worksheet.Range["B1"].Value = "'=" + formula;
   for (var i = 0; i < displayItems.Count; i++)
   {
      var item = displayItems[i];
      var text = "'" + item.TypeName;

      worksheet.Range[i + 4, 0].Value = text;
      worksheet.Range[i + 4, 0].IndentLevel = item.IndentLevel;
      worksheet.Range[i + 4, 1].Value = "'" + item.Content;
   }

   //Apply styling
   worksheet.Range["A1:B3"].Interior.Color = System.Drawing.Color.FromArgb(68, 114, 196);
   worksheet.Range["A1:B3"].Font.Color = System.Drawing.Color.White;
   worksheet.Range["A1:B3"].Borders.Color = System.Drawing.Color.FromArgb(91, 155, 213);
   worksheet.Range["A1:B3"].Borders.LineStyle = BorderLineStyle.Thin;
   worksheet.Range["A1,A3,B3"].Font.Size = 14;
   worksheet.Range["A1,A3,B3"].Font.Bold = true;
   worksheet.Range["A:C"].EntireColumn.AutoFit();           
}

下图是生成的 FormulaSyntaxTree 的效果图图。请注意,这只是完整语法树的一部分:

4、修改公式

从上一步生成的语法树中,您可以看到销售代表姓名以 TextNode 形式表示,并且在公式中多次出现。我们可以通过简单的查找和替换操作来替换所有这些出现的情况,如下面的代码所示:

下面的代码包含一些格式化代码来格式化销售报告内容。

//Method to parse and modify the formula
public static void ModifyFormula(IWorksheet worksheet, string originalFormula)
{
    //Apply UNIQUE formula to get unique sales representatives list
    worksheet.Range["F1"].Value = "Unique Rep";
    worksheet.Range["F2"].Formula = "=UNIQUE(A2:A19)";
    var uniqueRep = worksheet.Range["F2#"];
    // Apply Styling
    worksheet.Range["F:F"].EntireColumn.AutoFit();
    worksheet.Range["F1"].Interior.Color = System.Drawing.Color.FromArgb(68, 114, 196);
    worksheet.Range["F1"].Font.Color = System.Drawing.Color.White;
    worksheet.Range["F2#"].Borders.Color = System.Drawing.Color.FromArgb(91, 155, 213);
    worksheet.Range["F2#"].Borders.LineStyle = BorderLineStyle.Thin;

    //Get syntax tree
    var syntaxTree = FormulaSyntaxTree.Parse(originalFormula);

    //Find
    var findText = new TextNode("Fritz");

    //Replacement
    var replaceText = new TextNode("");

    //Loop through names list to modify the formula for each sales representative
    for (int r = 0, resultRow = 3; r < uniqueRep.Cells.Count; r++, resultRow = resultRow + 4)
    {
       //Get name to be replaced in the formula
       var cval = uniqueRep.Cells[r].Value.ToString();

       if (findText.Value != cval)
       {
          //Assign name to be replaced to Replace TextNode
          replaceText.Value = cval;

          //Invoke the recursive method to perform find and replace operation
          replaceNode(syntaxTree.Root, findText, replaceText);

          //Assign the modified formula to a cell in the worksheet
          var resultRange = "H" + resultRow.ToString();
          worksheet.Range[resultRange].Formula = "=" + syntaxTree.ToString();
          worksheet.Range[resultRange + "#"].Borders.Color = System.Drawing.Color.FromArgb(91, 155, 213);
          worksheet.Range[resultRange + "#"].Borders.LineStyle = BorderLineStyle.Thin;

          //Update the value of Find node to perform find and replace operation for next sales representative name
          findText = replaceText;
       }
    }

    //Find and replace
    void replaceNode(SyntaxNode lookIn, SyntaxNode find, SyntaxNode replacement)
    {
       var children = lookIn.Children;

       for (var i = 0; i < children.Count; i++)
       {
          var child = children[i];
          if (child.Equals(find))
          {
             children[i] = replacement;
          }
          else
          {
             replaceNode(child, find, replacement);
          }
       }
    }
 }

这是修改后的公式之一:

=IF(ISNUMBER(FILTER(A2:D19,A2:A19="Xi")),IFS(FILTER(A2:D19,A2:A19="Xi")>5000,"Above Target",FILTER(A2:D19,A2:A19="Xi")>3000,"On Target",FILTER(A2:D19,A2:A19="Xi")<2500,"Below Target"),FILTER(A2:D19,A2:A19="Xi"))

5、保存 Excel 文件

将所有修改的公式添加到工作表后,将调用 Workbook 类的 Save 方法来保存 Excel 文件,如下面的代码所示:

//Save modified Excel file
workbook.Save("ModifiedFormula.xlsx", SaveFileFormat.Xlsx);

打开保存的 Excel 文件可以看到下图:

到此这篇关于使用C#实现解析Excel公式的文章就介绍到这了,更多相关C#解析Excel公式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文