检索电子表格文档中单元格的值

本主题演示如何使用 Open XML SDK for Office 中的类以编程方式检索电子表格文档中单元格的值。 本文包含一个演示此任务的示例 GetCellValue 方法。

GetCellValue 方法

可以使用 GetCellValue 方法检索工作簿中单元格的值。 该方法需要以下三个参数:

  • 一个包含要检查的文档的名称的字符串。

  • 一个包含要检查的工作表的名称的字符串。

  • 一个包含要从中检索值的单元格地址(例如 A1、B12)的字符串。

如果可以找到指定单元格的值,则该方法将返回该值。 以下示例代码显示方法签名。

static string GetCellValue(string fileName, string sheetName, string addressName)

代码的工作方式

该代码先创建一个放置返回值的变量,然后将其初始化为空。

string? value = null;

访问单元格

接下来,代码使用 Open 方法打开文档,指示文档应打开,以便进行只读访问, (最终 的 false 参数) 。 接下来,代码使用文档的 WorkbookPart 属性检索对工作簿部件的引用。

// Open the spreadsheet document for read-only access.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, false))
{
    // Retrieve a reference to the workbook part.
    WorkbookPart? wbPart = document.WorkbookPart;

为查找请求的单元格,该代码必须先检索对工作表的引用(假定已提供其名称)。 代码必须搜索工作簿部件工作簿元素的所有工作表类型后代,并检查它找到的每个工作表的 Name 属性。 请注意,此搜索只会检查工作簿的关系,而不会实际查找工作表部分。 它查找对 工作表的引用,其中包含工作表的名称和 ID 等信息。 为此,最简单的方法是使用 LINQ 查询,如下面的代码示例所示。

// Find the sheet with the supplied name, and then use that 
// Sheet object to retrieve a reference to the first worksheet.
Sheet? theSheet = wbPart?.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).FirstOrDefault();

// Throw an exception if there is no sheet.
if (theSheet is null || theSheet.Id is null)
{
    throw new ArgumentException("sheetName");
}

请注意,FirstOrDefault 方法将返回第一个匹配引用(在此示例中,为一个工作表)或一个空引用(如果未找到匹配项)。 代码检查 null 引用,如果传入了无效的工作表名称,则会引发异常。现在,你已获得有关工作表的信息,代码必须检索对相应工作表部分的引用。 已检索的工作表信息提供 Id 属性,并且给定该 Id 属性,代码可以通过调用工作簿部件 GetPartById 方法检索对相应 WorksheetPart 的引用。

// Retrieve a reference to the worksheet part.
WorksheetPart wsPart = (WorksheetPart)wbPart!.GetPartById(theSheet.Id!);

与查找命名工作表时一样,在查找命名单元格时,代码使用 Descendants 方法,搜索 CellReference 属性等于指定 addressName 参数的第一个匹配项。 在此方法调用后,名为 theCell 的变量将包含对单元格的引用或包含空引用。

// Use its Worksheet property to get a reference to the cell 
// whose address matches the address you supplied.
Cell? theCell = wsPart.Worksheet?.Descendants<Cell>()?.Where(c => c.CellReference == addressName).FirstOrDefault();

检索值

此时,名为 theCell 的变量将包含空引用或包含对请求的单元格的引用。 如果您检查该单元格的 Open XML 内容(即 theCell.OuterXml),将会发现与以下示例代码类似的 XML。

    <x:c r="A1">
        <x:v>12.345000000000001</x:v>
    </x:c>

InnerText 属性包含单元格的内容,因此下一个代码块将检索此值。

// If the cell does not exist, return an empty string.
if (theCell is null || theCell.InnerText.Length < 0)
{
    return string.Empty;
}
value = theCell.InnerText;

现在,示例方法必须解释该值。 实际上,该代码将处理数字和日期、字符串和布尔值。 必要时,可扩展示例。 Cell 类型提供一个 DataType 属性,该属性指示单元格中的数据类型。 对于数值和日期类型,DataType 属性的值为 null。 它包含字符串的 CellValues.SharedString 值和 布尔值的 CellValues.Boolean 值。 如果 DataType 属性为空,则该代码会返回单元格的值(一个数值)。 否则,代码将根据数据类型来进行分支以继续。

// If the cell represents an integer number, you are done. 
// For dates, this code returns the serialized value that 
// represents the date. The code handles strings and 
// Booleans individually. For shared strings, the code 
// looks up the corresponding value in the shared string 
// table. For Booleans, the code converts the value into 
// the words TRUE or FALSE.
if (theCell.DataType is not null)
{
    if (theCell.DataType.Value == CellValues.SharedString)
    {

如果 DataType 属性包含 CellValues.SharedString,则代码必须检索对单个 SharedStringTablePart 的引用。

// For shared strings, look up the value in the
// shared strings table.
var stringTable = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();

接下来,如果字符串表存在(如果不存在,则表示工作簿损坏,并且示例代码会将索引返回到字符串表中而非字符串本身),则代码返回它在指定索引处发现的元素的 InnerText 属性(先将值属性转换为整数)。

// If the shared string table is missing, something 
// is wrong. Return the index that is in
// the cell. Otherwise, look up the correct text in 
// the table.
if (stringTable is not null)
{
    value = stringTable.SharedStringTable.ElementAt(int.Parse(value)).InnerText;
}

如果 DataType 属性包含 CellValues.Boolean,则该代码将它在单元格值中找到的 0 或 1 转换为相应的文本字符串。

switch (value)
{
    case "0":
        value = "FALSE";
        break;
    default:
        value = "TRUE";

最后,该过程将返回变量 value,其中包含请求的信息。

示例代码

下面是 C# 和 Visual Basic 中的完整 GetCellValue 示例代码。

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Linq;

Console.WriteLine(GetCellValue(args[0], args[1], args[2]));
// Retrieve the value of a cell, given a file name, sheet name, 
// and address name.
static string GetCellValue(string fileName, string sheetName, string addressName)
{
    string? value = null;
    // Open the spreadsheet document for read-only access.
    using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, false))
    {
        // Retrieve a reference to the workbook part.
        WorkbookPart? wbPart = document.WorkbookPart;
        // Find the sheet with the supplied name, and then use that 
        // Sheet object to retrieve a reference to the first worksheet.
        Sheet? theSheet = wbPart?.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName).FirstOrDefault();

        // Throw an exception if there is no sheet.
        if (theSheet is null || theSheet.Id is null)
        {
            throw new ArgumentException("sheetName");
        }
        // Retrieve a reference to the worksheet part.
        WorksheetPart wsPart = (WorksheetPart)wbPart!.GetPartById(theSheet.Id!);
        // Use its Worksheet property to get a reference to the cell 
        // whose address matches the address you supplied.
        Cell? theCell = wsPart.Worksheet?.Descendants<Cell>()?.Where(c => c.CellReference == addressName).FirstOrDefault();
        // If the cell does not exist, return an empty string.
        if (theCell is null || theCell.InnerText.Length < 0)
        {
            return string.Empty;
        }
        value = theCell.InnerText;
        // If the cell represents an integer number, you are done. 
        // For dates, this code returns the serialized value that 
        // represents the date. The code handles strings and 
        // Booleans individually. For shared strings, the code 
        // looks up the corresponding value in the shared string 
        // table. For Booleans, the code converts the value into 
        // the words TRUE or FALSE.
        if (theCell.DataType is not null)
        {
            if (theCell.DataType.Value == CellValues.SharedString)
            {
                // For shared strings, look up the value in the
                // shared strings table.
                var stringTable = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
                // If the shared string table is missing, something 
                // is wrong. Return the index that is in
                // the cell. Otherwise, look up the correct text in 
                // the table.
                if (stringTable is not null)
                {
                    value = stringTable.SharedStringTable.ElementAt(int.Parse(value)).InnerText;
                }
            }
            else if (theCell.DataType.Value == CellValues.Boolean)
            {
                switch (value)
                {
                    case "0":
                        value = "FALSE";
                        break;
                    default:
                        value = "TRUE";
                        break;
                }
            }
        }
    }

    return value;
}