Неофициальный русский перевод. Первоисточник: Microsoft Learn.

Получение значений ячеек в документе электронной таблицы

В этом разделе показано, как использовать классы Open XML SDK для Office, чтобы программно получать значения ячеек в документе электронной таблицы. В нём содержится пример метода GetCellValue, иллюстрирующий эту задачу.

Метод GetCellValue

Метод GetCellValue можно использовать для получения значения ячейки в книге. Метод требует следующие три параметра:

Метод возвращает значение указанной ячейки, если её удалось найти. В следующем примере кода показана сигнатура метода.

static string GetCellValue(string fileName, string sheetName, string addressName)
Function GetCellValue(fileName As String, sheetName As String, addressName As String) As String

Как работает код

Код начинается с создания переменной для хранения возвращаемого значения и инициализирует её значением null.

string? value = null;
Dim value As String = Nothing

Доступ к ячейке

Далее код открывает документ с помощью метода 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;
' Open the spreadsheet document for read-only access.
Using document As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, False)
    ' Retrieve a reference to the workbook part.
    Dim wbPart As WorkbookPart = 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");
}
' Find the sheet with the supplied name, and then use that 
' Sheet object to retrieve a reference to the first worksheet.
Dim theSheet As Sheet = wbPart?.Workbook.Descendants(Of Sheet)().Where(Function(s) s.Name = sheetName).FirstOrDefault()

' Throw an exception if there is no sheet.
If theSheet Is Nothing OrElse theSheet.Id Is Nothing Then
    Throw New ArgumentException("sheetName")
End If

Учтите, что метод FirstOrDefault возвращает либо первую подходящую ссылку (в данном случае лист), либо нулевую ссылку, если соответствий не найдено. Код проверяет нулевую ссылку и выбрасывает исключение, если передано недопустимое имя листа. Теперь, когда имеются сведения о листе, код должен получить ссылку на соответствующую часть пакета рабочего листа. Уже полученные сведения о листе предоставляют свойство Id, и по этому свойству Id код может получить ссылку на соответствующий WorksheetPart, вызвав метод GetPartById части пакета книги.

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

Как и при поиске именованного листа, при поиске именованной ячейки код использует метод 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();
' Use its Worksheet property to get a reference to the cell 
' whose address matches the address you supplied.
Dim theCell As Cell = wsPart.Worksheet?.Descendants(Of Cell)().Where(Function(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;
' If the cell does not exist, return an empty string.
If theCell Is Nothing OrElse theCell.InnerText.Length < 0 Then
    Return String.Empty
End If
value = theCell.InnerText

Теперь демонстрационный метод должен интерпретировать значение. В текущем виде код обрабатывает числовые значения, даты, строки и логические значения. Демонстрационный пример можно расширить по необходимости. Тип Cell предоставляет свойство DataType, которое указывает тип данных в ячейке. Значение свойства DataType равно null для числовых типов и типов дат. Для строк оно содержит значение CellValues.SharedString, а для логических значений — CellValues.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)
    {
' 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 IsNot Nothing Then
    If theCell.DataType.Value = CellValues.SharedString Then

Если свойство DataType содержит CellValues.SharedString, код должен получить ссылку на единственную SharedStringTablePart.

// For shared strings, look up the value in the
// shared strings table.
var stringTable = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
' For shared strings, look up the value in the
' shared strings table.
Dim stringTable = wbPart.GetPartsOfType(Of 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;
}
' 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 IsNot Nothing Then
    value = stringTable.SharedStringTable.ElementAt(Integer.Parse(value)).InnerText
End If

Если свойство DataType содержит CellValues.Boolean, код преобразует найденные в значении ячейки 0 или 1 в соответствующую текстовую строку.

switch (value)
{
    case "0":
        value = "FALSE";
        break;
    default:
        value = "TRUE";
        break;
}
Select Case value
    Case "0"
        value = "FALSE"
    Case Else
        value = "TRUE"
End Select

Наконец, процедура возвращает переменную value, содержащую запрошенные сведения.

Пример кода

Ниже приведён полный пример кода GetCellValue на C# и Visual Basic.

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;
}
Function GetCellValue(fileName As String, sheetName As String, addressName As String) As String
    Dim value As String = Nothing
    ' Open the spreadsheet document for read-only access.
    Using document As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, False)
        ' Retrieve a reference to the workbook part.
        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 first worksheet.
        Dim theSheet As Sheet = wbPart?.Workbook.Descendants(Of Sheet)().Where(Function(s) s.Name = sheetName).FirstOrDefault()

        ' Throw an exception if there is no sheet.
        If theSheet Is Nothing OrElse theSheet.Id Is Nothing Then
            Throw New ArgumentException("sheetName")
        End If
        ' Retrieve a reference to the worksheet part.
        Dim wsPart As WorksheetPart = CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
        ' Use its Worksheet property to get a reference to the cell 
        ' whose address matches the address you supplied.
        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 Is Nothing OrElse theCell.InnerText.Length < 0 Then
            Return String.Empty
        End If
        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 IsNot Nothing Then
            If theCell.DataType.Value = CellValues.SharedString Then
                ' 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 is 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
            ElseIf theCell.DataType.Value = CellValues.Boolean Then
                Select Case value
                    Case "0"
                        value = "FALSE"
                    Case Else
                        value = "TRUE"
                End Select
            End If
        End If
    End Using

    Return value
End Function

См. также

Лицензия исходного репозитория Microsoft (оригинал)

    MIT License

    Copyright (c) Microsoft Corporation. All rights reserved.

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE