wake-up-neo.net

Wie durchlaufe ich Zeilen in einer Excel-Tabelle mit Epplus?

Ich bin neu bei epplus und versuche, einige Werte aus einer Excel-Tabelle zu lesen.

Das habe ich bisher:

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)  // <-- !!
             { ... }
        }
    }
}

Jetzt bin ich jedoch verblüfft, da ExcelTable nur eine Columns -Eigenschaft hat, nicht jedoch eine Rows-Eigenschaft, wie ich erwartet hatte. Ich kann keine Rows-Eigenschaft für jedes Objekt in der Bibliothek finden.

Wie durchlaufe ich eine Tabelle und lese Zeile für Zeile?

40
oɔɯǝɹ

Bei der Suche nach Hilfe für dasselbe Problem bin ich auf diesen link gestoßen. Es hat sicherlich für mich funktioniert! Definitiv besser als die Verwendung von Interop-Objekten. :)

Ich habe es etwas angepasst:

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

So erhalten Sie eine vollständige Zeile als ExcelRange, die dann wiederholt oder für LINQ verwendet werden kann:

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

Sie können auf die .Worksheet-Eigenschaft einer Tabelle zugreifen und deren Zellen indizieren. Ich habe zu diesem Zweck eine Erweiterungsmethode geschrieben, die eine Reihe von Wörterbüchern generiert, die den Spaltennamen und den Zellenwert zuordnen:

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

Ich bin nicht sicher über Epplus, aber ich dachte, ich würde schnell einen Vorschlag machen, LinqToExcel zu verwenden.

var Excel = new ExcelQueryFactory(Excel);

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

sie können es von NuGet bekommen 

Install-Package LinqToExcel
2
Zach Spencer

Ich habe auch versucht herauszufinden, wie ich die Objekte richtig durchlaufen kann und an die Daten komme, die ich mit dieser API benötige. 

Ich habe Informationen aus verschiedenen Beiträgen und die Einstiegsseite des Autors gesammelt und alles zusammengestellt, um mir und anderen zu helfen. 

Das Hauptproblem ist Ihr Einstiegspunkt für die Iteration. Die meisten Lösungen, die ich gesehen habe, gehen nach dem Arbeitsblatt weiter, während diese Frage spezifisch für den Tisch ist. Ich war neugierig auf beide und präsentiere meine Ergebnisse auf beiden Seiten.

Arbeitsblatt-Beispiel:

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;
                }
            }
        }
    }
}

Jetzt können die Dinge hier ein bisschen durcheinander kommen, für mich hatte ich zumindest keine Tische, mit denen ich anfangen konnte. Unter der gleichen Anweisung mit der Anweisung, wenn ich zuerst die Arbeitsblattzellen durchlaufen und dann irgendetwas mit der Tables-Eigenschaft anfassen würde, wurde eine Ausnahme ausgelöst. Wenn ich ein Paket erneut instanziiere und denselben/ähnlichen Code verwende, wird es nicht explodieren, wenn ich sehe, ob wir Tabellen haben oder nicht.

Tabellenbeispiel:

//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;
            }
        }
    }
}

Im Wesentlichen funktioniert und funktioniert alles genauso, man muss nur nach dem Kind gehen. Parent, AKA table.WorkSheet, um das gleiche Zeug zu bekommen. Wie andere bereits erwähnt haben, können Erweiterungsmethoden und möglicherweise sogar Wrapper-Klassen zu mehr Granularität führen, je nach den spezifischen Anforderungen Ihres Unternehmens. Dies war jedoch nicht der Zweck dieser Frage.

In Bezug auf die Indexierungskommentare und -antworten würde ich empfehlen, bei den Eigenschaften 'Row' und 'Column' (First, Last, Foreach usw.) zu bleiben Ausgabe hier mindestens mit der neuen Version. 

1
UberBiza

Ich hatte das gleiche Problem und löste es unter Verwendung der Variablen ExcelTable, um die Tabellengrenze zu erhalten, und ExcelWorksheet, um die Daten abzurufen. Ihr Code sieht also ungefähr so ​​aus:

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];
                ...
            }
        }
    }
}

Sie müssen nach Tabellenkopfzeilen oder anderen Dingen suchen, aber das hat mir geholfen.

0
Dave Savage