Share via


Obtención de un encabezado de columna en un documento de hoja de cálculo

En este tema se muestra cómo usar las clases del SDK de Open XML para Office para recuperar un encabezado de columna en un documento de hoja de cálculo mediante programación.

Estructura básica de un documento spreadsheetML

La estructura de documento básica de un documento SpreadsheetML se compone de elementos Sheets y Sheet, que hacen referencia a las hojas de cálculo en el libro. Se crea un archivo XML independiente para cada hoja de cálculo. Por ejemplo, SpreadsheetML para un Workbook que tiene dos hojas de cálculo MySheet1 y MySheet2 se encuentra en el archivo Workbook.xml y se muestra en el siguiente ejemplo de código.

    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
    <workbook xmlns=https://schemas.openxmlformats.org/spreadsheetml/2006/main xmlns:r="https://schemas.openxmlformats.org/officeDocument/2006/relationships">
        <sheets>
            <sheet name="MySheet1" sheetId="1" r:id="rId1" /> 
            <sheet name="MySheet2" sheetId="2" r:id="rId2" /> 
        </sheets>
    </workbook>

Los archivos XML de hoja de cálculo contienen uno o varios elementos de nivel de bloque, como sheetData , que representa la tabla de celdas y contiene uno o varios elementos Row . Un elemento row contiene uno o más elementos Cell. Cada celda contiene un elemento CellValue que representa el valor de la celda. Por ejemplo, el SpreadsheetML para la primera hoja de cálculo del libro, que solo tiene el valor 100 en la celda A1, se encuentra en el archivo Sheet1.xml y se muestra en el siguiente ejemplo de código.

    <?xml version="1.0" encoding="UTF-8" ?> 
    <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
        <sheetData>
            <row r="1">
                <c r="A1">
                    <v>100</v> 
                </c>
            </row>
        </sheetData>
    </worksheet>

Con el SDK de Open XML, puede crear la estructura de documentos y el contenido que usa clases fuertemente tipadas que corresponden a elementos SpreadsheetML . Puede encontrar estas clases en el espacio de nombres DocumentFormat.OpenXML.Spreadsheet. En la tabla siguiente se enumeran los nombres de las clases que corresponden a los elementos workbook, sheets, sheet, worksheet y sheetData.

Elemento de SpreadsheetML Open XML SDK (clase) Descripción
libro de trabajo DocumentFormat.OpenXML.Spreadsheet.Workbook El elemento raíz del elemento de documento principal.
sheets DocumentFormat.OpenXML.Spreadsheet.Sheets El contenedor de las estructuras del nivel de bloque, como sheet, fileVersion y otras que se detallan en la especificación ISO/IEC 29500.
sheet DocumentFormat.OpenXml.Spreadsheet.Sheet Una hoja que apunta a un archivo de definición de hoja.
hoja de cálculo DocumentFormat.OpenXML.Spreadsheet. Worksheet Un archivo de definición de hoja que contiene los datos de la hoja.
sheetData DocumentFormat.OpenXML.Spreadsheet.SheetData La tabla de celdas agrupadas por filas.
row DocumentFormat.OpenXml.Spreadsheet.Row Una fila en una tabla de celdas.
c DocumentFormat.OpenXml.Spreadsheet.Cell Una celda en una fila.
v DocumentFormat.OpenXml.Spreadsheet.CellValue El valor de una celda.

Funcionamiento del código de ejemplo

El código de este tema sobre procedimientos consta de tres métodos (funciones en Visual Basic): GetColumnHeading, GetColumnName y GetRowIndex. Se llama a los dos últimos métodos desde el método GetColumnHeading.

El método GetColumnName toma el nombre de celda como parámetro. Analiza el nombre de la celda para obtener el nombre de columna mediante la creación de una expresión regular para que coincida con la parte del nombre de columna del nombre de la celda. Para obtener más información sobre las expresiones regulares, vea Elementos de lenguaje de expresiones regulares.

// Create a regular expression to match the column name portion of the cell name.
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);

return match.Value;

El método GetRowIndex toma el nombre de celda como parámetro. Analiza el nombre de celda para obtener el índice de fila mediante la creación de una expresión regular para que coincida con la parte del nombre de celda que corresponde al índice de fila.

// Create a regular expression to match the row index portion the cell name.
Regex regex = new Regex(@"\d+");
Match match = regex.Match(cellName);

return uint.Parse(match.Value);

El método GetColumnHeading usa tres parámetros, la ruta de acceso completa al archivo de hoja de cálculo de origen, el nombre de la hoja de cálculo que contiene la columna especificada y el nombre de una celda de la columna para la que se desea obtener el encabezado.

El código obtiene el nombre de la columna de la celda especificada mediante una llamada al método GetColumnName. El código también obtiene las celdas de la columna y las ordena por fila mediante el método GetRowIndex.

// Get the column name for the specified cell.
string columnName = GetColumnName(cellName);

// Get the cells in the specified column and order them by row.
IEnumerable<Cell> cells = worksheetPart.Worksheet.Descendants<Cell>().Where(c => string.Compare(GetColumnName(c.CellReference?.Value), columnName, true) == 0)
    .OrderBy(r => GetRowIndex(r.CellReference) ?? 0);

Si la columna especificada existe, obtiene la primera celda de la columna mediante el método IEnumerable(T).First. La primera celda contiene el encabezado. De lo contrario, la columna especificada no existe y el método devuelve null / Nothing

if (cells.Count() == 0)
{
    // The specified column does not exist.
    return null;
}

// Get the first cell in the column.
Cell headCell = cells.First();

Si el contenido de la celda se encuentra almacenado en el objeto SharedStringTablePart, obtiene los elementos de cadena compartida y devuelve el contenido del encabezado de columna mediante el método M:System.Int32.Parse(System.String). Si el contenido de la celda no está en el objeto SharedStringTable, devuelve el contenido de la celda.

// If the content of the first cell is stored as a shared string, get the text of the first cell
// from the SharedStringTablePart and return it. Otherwise, return the string value of the cell.
if (headCell.DataType is not null && headCell.DataType.Value == CellValues.SharedString && int.TryParse(headCell.CellValue?.Text, out int index))
{
    SharedStringTablePart shareStringPart = document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
    SharedStringItem[] items = shareStringPart.SharedStringTable.Elements<SharedStringItem>().ToArray();

    return items[index].InnerText;
}
else
{
    return headCell.CellValue?.Text;
}

Código de ejemplo

A continuación se incluye el código de ejemplo completo en C# y Visual Basic.

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;


// Given a document name, a worksheet name, and a cell name, gets the column of the cell and returns
// the content of the first cell in that column.
static string? GetColumnHeading(string docName, string worksheetName, string cellName)
{
    // Open the document as read-only.
    using (SpreadsheetDocument document = SpreadsheetDocument.Open(docName, false))
    {
        IEnumerable<Sheet>? sheets = document.WorkbookPart?.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName);

        if (sheets is null || sheets.Count() == 0)
        {
            // The specified worksheet does not exist.
            return null;
        }

        string? id = sheets.First().Id;

        if (id is null)
        {
            // The worksheet does not have an ID.
            return null;
        }

        WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart!.GetPartById(id);

        // Get the column name for the specified cell.
        string columnName = GetColumnName(cellName);

        // Get the cells in the specified column and order them by row.
        IEnumerable<Cell> cells = worksheetPart.Worksheet.Descendants<Cell>().Where(c => string.Compare(GetColumnName(c.CellReference?.Value), columnName, true) == 0)
            .OrderBy(r => GetRowIndex(r.CellReference) ?? 0);

        if (cells.Count() == 0)
        {
            // The specified column does not exist.
            return null;
        }

        // Get the first cell in the column.
        Cell headCell = cells.First();

        // If the content of the first cell is stored as a shared string, get the text of the first cell
        // from the SharedStringTablePart and return it. Otherwise, return the string value of the cell.
        if (headCell.DataType is not null && headCell.DataType.Value == CellValues.SharedString && int.TryParse(headCell.CellValue?.Text, out int index))
        {
            SharedStringTablePart shareStringPart = document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
            SharedStringItem[] items = shareStringPart.SharedStringTable.Elements<SharedStringItem>().ToArray();

            return items[index].InnerText;
        }
        else
        {
            return headCell.CellValue?.Text;
        }
    }
}
// Given a cell name, parses the specified cell to get the column name.
static string GetColumnName(string? cellName)
{
    if (cellName is null)
    {
        return string.Empty;
    }

    // Create a regular expression to match the column name portion of the cell name.
    Regex regex = new Regex("[A-Za-z]+");
    Match match = regex.Match(cellName);

    return match.Value;
}

// Given a cell name, parses the specified cell to get the row index.
static uint? GetRowIndex(string? cellName)
{
    if (cellName is null)
    {
        return null;
    }

    // Create a regular expression to match the row index portion the cell name.
    Regex regex = new Regex(@"\d+");
    Match match = regex.Match(cellName);

    return uint.Parse(match.Value);
}