利用 Open XML 文件格式,可以检索有关 Excel 工作簿中特定单元格的信息。Open XML SDK 2.0 添加了强类型类以简化对 Open XML 文件格式的访问:SDK 简化了检索工作簿相关信息的任务,并会查找适当的 XML 内容。此直观操作方法附带的代码示例演示如何使用 SDK 执行此任务。
此直观操作方法附带的代码示例包括检索 Excel 2007 或 Excel 2010 工作簿中指定工作表中的指定单元格的值所需的代码。
设置引用
若要使用 Open XML SDK 2.0 中的代码,您必须向您的项目添加多个引用。虽然示例项目包含这些引用,但您必须在您的代码中显式引用以下程序集:
您还应将下面的 using/Imports 语句添加到代码文件的顶部。
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
检查过程
XLGetCellValue 过程接受三个参数,这些参数指示以下内容:
Public Function XLGetCellValue(ByVal fileName As String,
ByVal sheetName As String, ByVal addressName As String) As String
public static string XLGetCellValue(string fileName,
string sheetName, string addressName)
如果能找到指定单元格的值,则此过程将返回该值。若要调用该过程,请传递所有参数值,如下面的代码示例所示。
Const fileName As String = "C:\temp\GetCellValue.xlsx"
Dim value As String = XLGetCellValue(fileName, "Sheet1", "B3")
const string fileName = @"C:\temp\GetCellValue.xlsx";
string value = XLGetCellValue(fileName, "Sheet1", "A1");
访问单元格
下面的代码示例演示代码如何首先创建将包含返回值的变量,并假定结果为 Null。
Dim value as String = Nothing
string value = null;
接下来,此代码将使用 SpreadsheetDocument.Open 方法打开文档,并指示应打开此文档以供只读访问(最后的 false 参数)。紧接着,此代码将使用文档的 WorkbookPart 属性来检索对工作簿部分的引用。
Using document As SpreadsheetDocument =
SpreadsheetDocument.Open(fileName, False)
Dim wbPart As WorkbookPart = document.WorkbookPart
' Code removed here…
End Using
using (SpreadsheetDocument document =
SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart wbPart = document.WorkbookPart;
// Code removed here…
}
若要查找请求的单元格,则代码必须先检索对工作表的引用(假定已提供其名称)。代码必须搜索工作簿部分 Workbook 属性的所有工作表类型后代,并检查它查找的每个工作表的 Name 属性。请注意,此搜索只会检查工作簿的关系,而不会实际查找工作簿部分 - 它仅查找对工作表的引用,其中包含工作表的名称和 ID 等信息。使用 LINQ 查询执行此操作最为简单。
Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)().
Where(Function(s) s.Name = sheetName).FirstOrDefault()
If theSheet Is Nothing Then
Throw New ArgumentException("sheetName")
End If
Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
Where(s => s.Name == sheetName).FirstOrDefault();
if (theSheet == null)
{
throw new ArgumentException("sheetName");
}
请注意,FirstOrDefault 方法将返回第一个匹配引用(在此示例中,为一个工作表)或一个空引用(如果未找到匹配项)。代码将检查空引用,如果您传递的工作表名无效,则将引发异常。
现在,您已具有有关工作表的信息,代码必须检索对相应的工作表部分的引用。您检索到的工作表信息提供了一个 Id 属性,利用此 Id 属性,代码可通过调用 WorkbookPartGetPartById 属性来检索对相应的 WorksheetPart 的引用。
Dim wsPart As WorksheetPart =
CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
WorksheetPart wsPart =
(WorksheetPart)(wbPart.GetPartById(theSheet.Id));
如同在查找命名工作表时一样,在查找命名单元格时,代码将使用 Descendants 方法来查找第一个匹配项,其中 Reference 属性等于指定的 addressName 参数。在此方法调用后,名为 theCell 的变量将包含对单元格的引用或包含空引用。
Dim theCell As Cell = wsPart.Worksheet.Descendants(Of Cell).
Where(Function(c) c.CellReference = addressName).FirstOrDefault
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 theCell IsNot Nothing Then
value = theCell.InnerText
' Code removed here…
End If
if (theCell != null)
{
value = theCell.InnerText;
// Code removed here…
}
现在,示例过程必须解释该值。实际上,此示例将处理数值、日期值、字符串值和布尔值 - 您可以在必要时扩展此示例。单元格类型提供了一个 DataType 属性,该属性指示单元格内的数据类型;对于数字和日期类型,DataType 属性的值为 null。它包含值 CellValues.SharedString(对于字符串)和 CellValues.Boolean(对于布尔值)。如果 DataType 属性为 null,则代码仅返回该单元格的值(一个数值)。否则,代码将根据数据类型来进行分支以继续。
If theCell.DataType IsNot Nothing Then
Select Case theCell.DataType.Value
' Code removed here…
End Select
End If
if (theCell.DataType != null)
{
switch (theCell.DataType.Value)
{
// Code removed here…
}
}
如果 DataType 属性包含 CellValues.SharedString,则代码必须检索对单个 SharedStringTablePart 的引用。
Dim stringTable = wbPart.
GetPartsOfType(Of SharedStringTablePart).FirstOrDefault()ws.Save()
var stringTable =
wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
接下来,如果有字符串表,则代码将返回它在指定索引处找到的元素的 InnerText 属性(首先会将值属性转换为整数)。
> [!NOTE]
> 如果没有字符串表,则表示工作簿已损坏,并且示例代码会将索引返回到字符串表中而非字符串本身。
If stringTable IsNot Nothing Then
value = stringTable.SharedStringTable.
ElementAt(Integer.Parse(value)).InnerText
End If
if (stringTable != null)
{
value = stringTable.SharedStringTable.
ElementAt(int.Parse(value)).InnerText;
}
如果 DataType 属性包含 CellValues.Boolean,则代码会将它在单元格值中找到的 0 或 1 转换为适当的文本字符串。
Case CellValues.Boolean
Select Case value
Case "0"
value = "FALSE"
Case Else
value = "TRUE"
End Select
case CellValues.Boolean:
switch (value)
{
case "0":
value = "FALSE";
break;
default:
value = "TRUE";
break;
}
最后,该过程将返回变量 value,其中包含请求的信息。
示例过程
下面的代码示例演示完整的过程。
Public Function XLGetCellValue(ByVal fileName As String,
ByVal sheetName As String, ByVal addressName As String) As String
Dim value As String = Nothing
Using document As SpreadsheetDocument =
SpreadsheetDocument.Open(fileName, False)
Dim wbPart As WorkbookPart = document.WorkbookPart
' Find the sheet with the supplied name, and then use that
' Sheet object to retrieve a reference to the appropriate
' worksheet.
Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)().
Where(Function(s) s.Name = sheetName).FirstOrDefault()
If theSheet Is Nothing Then
Throw New ArgumentException("sheetName")
End If
' Retrieve a reference to the worksheet part, and then use its
' Worksheet property to get a reference to the cell whose address
' matches the address you supplied:
Dim wsPart As WorksheetPart =
CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
Dim theCell As Cell = wsPart.Worksheet.Descendants(Of Cell).
Where(Function(c) c.CellReference = addressName).FirstOrDefault
' If the cell does not exist, return an empty string.
If theCell IsNot Nothing Then
value = theCell.InnerText
' If the cell represents an numeric value, 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 IsNot Nothing Then
Select Case theCell.DataType.Value
Case CellValues.SharedString
' For shared strings, look up the value in the shared
' strings table.
Dim stringTable = wbPart.
GetPartsOfType(Of SharedStringTablePart).FirstOrDefault()
' If the shared string table is missing, something is wrong.
' Return the index that you found in the cell.
' Otherwise, look up the correct text in the table.
If stringTable IsNot Nothing Then
value = stringTable.SharedStringTable.
ElementAt(Integer.Parse(value)).InnerText
End If
Case CellValues.Boolean
Select Case value
Case "0"
value = "FALSE"
Case Else
value = "TRUE"
End Select
End Select
End If
End If
End Using
Return value
End Function
public static string XLGetCellValue(
string fileName, string sheetName, string addressName)
{
string value = null;
using (SpreadsheetDocument document =
SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart wbPart = document.WorkbookPart;
// Find the sheet with the supplied name, and then use that Sheet
// object to retrieve a reference to the appropriate worksheet.
Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
Where(s => s.Name == sheetName).FirstOrDefault();
if (theSheet == null)
{
throw new ArgumentException("sheetName");
}
// Retrieve a reference to the worksheet part, and then use its
// Worksheet property to get a reference to the cell whose
// address matches the address you supplied:
WorksheetPart wsPart =
(WorksheetPart)(wbPart.GetPartById(theSheet.Id));
Cell theCell = wsPart.Worksheet.Descendants<Cell>().
Where(c => c.CellReference == addressName).FirstOrDefault();
// If the cell does not exist, return an empty string:
if (theCell != null)
{
value = theCell.InnerText;
// If the cell represents a numeric value, 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 != null)
{
switch (theCell.DataType.Value)
{
case 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 you found in the cell.
// Otherwise, look up the correct text in the table.
if (stringTable != null)
{
value = stringTable.SharedStringTable.
ElementAt(int.Parse(value)).InnerText;
}
break;
case CellValues.Boolean:
switch (value)
{
case "0":
value = "FALSE";
break;
default:
value = "TRUE";
break;
}
break;
}
}
}
}
return value;
}
此直观操作方法附带的示例包含一些代码,这些代码将检索 Excel 2007 或 Excel 2010 工作簿中指定工作表中的特定单元格的值。若要使用该代码示例,请安装 Open XML SDK 2.0(可通过"浏览"一节中列出的链接获得)。该示例还使用作为 Open XML SDK 2.0 的代码示例集的一部分包含的代码。"浏览"一节还包括指向完整代码示例集的链接,但您无需下载并安装示例代码即可使用示例。
示例应用程序将检索您提供的文档中多个单元格中的值,并在示例中调用 XLGetCellValue 方法来执行此操作。此方法会将指定单元格的值作为字符串返回 - 调用的代码必须解释字符串值。对此方法的调用类似于以下代码示例。
Const fileName As String = "C:\temp\GetCellValue.xlsx"
Dim value As String = XLGetCellValue(fileName, "Sheet1", "A1")
Console.WriteLine(value)
value = XLGetCellValue(fileName, "Sheet1", "A2")
Console.WriteLine(value)
value = XLGetCellValue(fileName, "Sheet1", "A3")
Console.WriteLine(value)
value = XLGetCellValue(fileName, "Sheet1", "A4")
Console.WriteLine(value)
const string fileName = @"C:\temp\GetCellValue.xlsx";
string value = XLGetCellValue(fileName, "Sheet1", "A1");
Console.WriteLine(value);
value = XLGetCellValue(fileName, "Sheet1", "A2");
Console.WriteLine(value);
value = XLGetCellValue(fileName, "Sheet1", "A3");
Console.WriteLine(value);
value = XLGetCellValue(fileName, "Sheet1", "A4");
Console.WriteLine(value);
了解 Excel 存储单元格及其值的方式非常重要。图 1 显示了一个示例工作簿,其中的四个单元格中具有四个类型的值 - 示例应用程序假定您已创建此工作簿并将其另存为 C:\temp\GetCellValue.xlsx。Open XML SDK 2.0 的工具目录中包含一个名为 OpenXmlSdkTool.exe 的有用的应用程序,如图 2 所示。利用此工具,您可以打开一个文档并查看该文档的各个部分及其层次结构。图 2 显示了测试文档 - 在左窗格中,已将文档扩展到工作表节点;在右窗格中,此工具显示了部分的 XML 和可用来生成部分的内容的反射 C# 代码。
图 1. Excel 工作簿中的示例数据
图 2 显示了 Open XML SDK 2.0 附带的 OpenXmlSdkTool.exe 应用程序。
图 2. Open XML SDK 2.0 Productivity Tool
如果您检查图 2 中的左窗格(部分的层次结构)和 XML 内容,您会发现一个事实,即您需要了解此直观操作方法中的代码:
无法使用 Productivity Tool 来查看对命名工作表的引用的检索,您必须先检查工作簿包含的所有工作表以查找与该名称匹配的项,并在给定工作表及其 ID 的情况下,检索工作表部分。该示例代码将为您解决这些问题。
Excel 仅保留一个部分来包含您在电子表格中使用的每个字符串。每个唯一的字符串仅在此表中出现一次,并且 Excel 会在此表中每个包含字符串的单元格中存储一个索引。在给定 CellValues.SharedString 的 DataType 属性的情况下,代码必须按索引检索共享字符串表部分中相应的共享字符串。图 3 显示了示例工作簿的共享字符串表的 Open XML SDK 2.0 Productivity Tool 表示形式。此示例包含两个字符串,其索引分别为 0 和 1。
图 3. 显示了示例工作表的共享字符串表的 Open XML SDK 2.0 Productivity Tool
|