检索电子表格文档中单元格的值
本主题演示如何使用 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;
}