web-dev-qa-db-fra.com

Comment puis-je parcourir les lignes d'un tableau Excel en utilisant epplus?

Je suis nouveau sur epplus , et j'essaie de lire certaines valeurs d'un tableau Excel.

C'est ce que j'ai jusqu'à présent:

var fileInfo = new FileInfo(filename);
using(var excelPackage = new OfficeOpenXml.ExcelPackage(fileInfo))
{
    foreach (var sheet in excelPackage.Workbook.Worksheets)
    {
        foreach (ExcelTable table in sheet.Tables)
        {
             foreach(var row in table.Rows)  // <-- !!
             { ... }
        }
    }
}

Cependant, je suis maintenant perplexe, car ExcelTable n'a qu'une propriété Columns, mais pas une propriété Rows comme je le pensais. Je ne trouve pas de propriété Rows sur un objet de la bibliothèque.

Comment parcourir une table en lisant Row pour Row?

40
oɔɯǝɹ

En cherchant de l'aide sur le même problème, je suis tombé sur ce lien link . Cela a certainement fonctionné pour moi! Certainement mieux que d’utiliser des objets Interop. :)

Je l'ai adapté légèrement cependant:

var package = new ExcelPackage(new FileInfo("sample.xlsx"));

ExcelWorksheet workSheet = package.Workbook.Worksheets[0];
var start = workSheet.Dimension.Start;
var end = workSheet.Dimension.End;
for (int row = start.Row; row <= end.Row; row++)
{ // Row by row...
    for (int col = start.Column; col <= end.Column; col++)
    { // ... Cell by cell...
        object cellValue = workSheet.Cells[row, col].Text; // This got me the actual value I needed.
    }
}
80
Chris Paton

Voici un moyen d'obtenir la ligne complète sous la forme ExcelRange qui peut ensuite être itérée ou utilisée pour LINQ:

for (var rowNum = 1; rowNum <= sheet.Dimension.End.Row; rowNum++)
{
    var row = sheet.Cells[string.Format("{0}:{0}", rowNum)];
    // just an example, you want to know if all cells of this row are empty
    bool allEmpty = row.All(c => string.IsNullOrWhiteSpace(c.Text));
    if (allEmpty) continue; // skip this row
    // ...
}
16
Rango

Vous pouvez accéder à la propriété .Worksheet d'une table et indexer ses cellules. J'ai écrit une méthode d'extension à cet effet, qui génère une série de dictionnaires mappant le nom de la colonne sur la valeur de la cellule:

public static IEnumerable<IDictionary<string, object>> GetRows(this ExcelTable table)
{
    var addr = table.Address;
    var cells = table.WorkSheet.Cells;

    var firstCol = addr.Start.Column;

    var firstRow = addr.Start.Row;
    if (table.ShowHeader)
        firstRow++;
    var lastRow = addr.End.Row;

    for (int r = firstRow; r <= lastRow; r++)
    {
        yield return Enumerable.Range(0, table.Columns.Count)
            .ToDictionary(x => table.Columns[x].Name, x => cells[r, firstCol + x].Value);
    }
}
10
AlexFoxGill

Je ne suis pas sûr de epplus, mais je pensais faire une suggestion rapide d'utilisation de LinqToExcel

var Excel = new ExcelQueryFactory(Excel);

var info = Excel.Worksheet("Sheet1")
                .Select(z=> new
                     {
                      Name = row["Name"].Cast<string>(),
                      Age = row["Age"].Cast<int>(),
                     }).ToList();

vous pouvez l'obtenir de NuGet 

Install-Package LinqToExcel
2
Zach Spencer

J'essayais également de comprendre comment parcourir correctement les objets et obtenir les données dont j'avais besoin avec cette API. 

J'ai recueilli des informations à partir de divers messages et de la page de démarrage de l'auteur et les ai rassemblées pour m'aider et aider les autres. 

Le problème principal est votre point d'entrée pour l'itération. La plupart des solutions que j'ai vues vont au-delà de la feuille de travail, alors que cette question est spécifique à la table, j'étais curieux de connaître les deux, alors je présente mes conclusions sur les deux.

Exemple de feuille de travail:

using (var package = new ExcelPackage(new FileInfo(file)))
{
    //what i've seen used the most, entry point is the worksheet not the table w/i the worksheet(s)
    using (var worksheet = package.Workbook.Worksheets.FirstOrDefault())
    {
        if (worksheet != null)
        {
            for (int rowIndex = worksheet.Dimension.Start.Row; rowIndex <= worksheet.Dimension.End.Row; rowIndex++)
            {
                var row = worksheet.Row(rowIndex);
                //from comments here... https://github.com/JanKallman/EPPlus/wiki/Addressing-a-worksheet
                //#:# gets entire row, A:A gets entire column
                var rowCells = worksheet.Cells[$"{rowIndex}:{rowIndex}"];
                //returns System.Object[,]
                //type is string so it likely detects many cells and doesn't know how you want the many formatted together...
                var rowCellsText = rowCells.Text;
                var rowCellsTextMany = string.Join(", ", rowCells.Select(x => x.Text));
                var allEmptyColumnsInRow = rowCells.All(x => string.IsNullOrWhiteSpace(x.Text));
                var firstCellInRowWithText = rowCells.Where(x => !string.IsNullOrWhiteSpace(x.Text)).FirstOrDefault();
                var firstCellInRowWithTextText = firstCellInRowWithText?.Text;
                var firstCellFromRow = rowCells[rowIndex, worksheet.Dimension.Start.Column];
                var firstCellFromRowText = firstCellFromRow.Text;
                //throws exception...
                //var badRow = rowCells[worksheet.Dimension.Start.Row - 1, worksheet.Dimension.Start.Column - 1];

                //for me this happened on row1 + row2 beign merged together for the column headers
                //not sure why the row.merged property is false for both rows though
                if (allEmptyColumnsInRow)
                    continue;

                for (int columnIndex = worksheet.Dimension.Start.Column; columnIndex <= worksheet.Dimension.End.Column; columnIndex++)
                {
                    var column = worksheet.Column(columnIndex);
                    var currentRowColumn = worksheet.Cells[rowIndex, columnIndex];
                    var currentRowColumnText = currentRowColumn.Text;
                    var currentRowColumnAddress = currentRowColumn.Address;
                    //likely won't need to do this, but i wanted to show you can tangent off at any level w/ that info via another call
                    //similar to row, doing A:A or B:B here, address is A# so just get first char from address
                    var columnCells = worksheet.Cells[$"{currentRowColumnAddress[0]}:{currentRowColumnAddress[0]}"];
                    var columnCellsTextMany = string.Join(", ", columnCells.Select(x => x.Text));
                    var allEmptyRowsInColumn = columnCells.All(x => string.IsNullOrWhiteSpace(x.Text));
                    var firstCellInColumnWithText = columnCells.Where(x => !string.IsNullOrWhiteSpace(x.Text)).FirstOrDefault();
                    var firstCellInColumnWithTextText = firstCellInColumnWithText?.Text;
                }
            }
        }
    }
}

Maintenant, les choses peuvent être un peu foirées ici, pour moi au moins je n'avais pas de table pour commencer. Dans le même package, avec using, si je devais d'abord parcourir les cellules de la feuille de calcul, puis toucher quoi que ce soit avec la propriété Tables, il levait une exception. Si je ré-instancie un paquet et utilise le même code/un code similaire, il n’explose pas lorsque nous voyons si nous avons des tables ou non.

Exemple de tableau:

//for some reason, if i don't instantiating another package and i work with the 'Tables' property in any way, the API throws a...
//Object reference not set to an instance of an object.
//at OfficeOpenXml.ExcelWorksheet.get_Tables()
//excetion... this is because i have data in my worksheet but not an actual 'table' (Excel => Insert => Table)
//a parital load of worksheet cell data + invoke to get non-existing tables must have a bug as below code does not
//throw an exception and detects null gracefully on firstordefault
using (var package = new ExcelPackage(new FileInfo(file)))
{
    //however, question was about a table, so lets also look at that... should be the same?
    //no IDisposable? :(
    //adding a table manually to my worksheet allows the 'same-ish' (child.Parent, aka table.WorkSheet) code to iterate
    var table = package.Workbook.Worksheets.SelectMany(x => x.Tables).FirstOrDefault();

    if (table != null)
    {
        for (int rowIndex = table.Address.Start.Row; rowIndex <= table.Address.End.Row; rowIndex++)
        {
            var row = table.WorkSheet.Row(rowIndex);

            var rowCells = table.WorkSheet.Cells[$"{rowIndex}:{rowIndex}"];
            var rowCellsManyText = string.Join(", ", rowCells.Select(x => x.Text));

            for (int columnIndex = table.Address.Start.Column; columnIndex <= table.Address.End.Column; columnIndex++)
            {
                var currentRowColumn = table.WorkSheet.Cells[rowIndex, columnIndex];
                var currentRowColumnText = currentRowColumn.Text;
            }
        }
    }
}

Essentiellement, tout fonctionne et fonctionne de la même manière, il vous suffit d'aller chercher child.Parent, AKA table.WorkSheet pour obtenir les mêmes éléments. Comme d'autres l'ont mentionné, les méthodes d'extension et peut-être même les classes wrapper pourraient vous donner plus de précision en fonction des besoins de votre entreprise, mais ce n'était pas le but de cette question.

En ce qui concerne les commentaires et les réponses d'indexation, je vous conseillerais de vous en tenir aux propriétés 'Row' et 'Column', d'abord, dernier, pour, foreach, etc. question ici au moins avec la nouvelle version. 

1
UberBiza

J'avais le même problème et je l'ai résolu en utilisant ExcelTable pour obtenir la limite de la table et ExcelWorksheet pour extraire les données. Donc, votre code ressemblera à quelque chose comme ça:

var fileInfo = new FileInfo(filename);
using(var excelPackage = new OfficeOpenXml.ExcelPackage(fileInfo))
{
    foreach (var sheet in excelPackage.Workbook.Worksheets)
    {
        foreach (ExcelTable table in sheet.Tables)
        {
            ExcelCellAddress start = table.Address.Start;
            ExcelCellAddress end = table.Address.End;

            for (int row = start.Row; row <= end.Row; ++row)
            {
                ExcelRange range = sheet.Cells[row, start.Column, row, end.Column];
                ...
            }
        }
    }
}

Vous devez vérifier l’en-tête du tableau ou d’autres choses, mais c’est ce qui m’a été.

0
Dave Savage