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

本主题演示如何使用 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 它找到的每个工作表的属性。 请注意,此搜索只会检查工作簿的关系,而不会实际查找工作表部分。 它会查找对 Sheet的引用,该引用包含工作表的名称和 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 引用。 代码检查 null 引用,如果传入了无效的工作表名称,则会引发异常。现在,你已获得有关工作表的信息,代码必须检索对相应工作表部分的引用。 已检索的工作表信息提供了一个Id属性,并且给定该 Id 属性,代码可以通过调用工作簿部件GetPartById方法检索对相应 WorksheetPart 的引用。

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

与查找命名工作表时一样,在查找命名单元格时,代码使用 Descendants 方法,搜索属性等于指定的第一个匹配 CellReferenceaddressName 参数。 在该方法调用后,名为 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.SharedStringCellValues.Boolean 布尔值的值。 DataType如果 属性为 null,则代码返回单元格的值, (它是数值) 。 否则,代码将根据数据类型来进行分支以继续。

// 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 它在指定索引处找到的元素的属性, (首先将 value 属性转换为整数) 。

// 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";
        break;
}

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

示例代码

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

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;
}

另请参阅