source

eplus를 사용하여 Excel 테이블의 행을 반복하려면 어떻게 해야 합니까?

nicesource 2023. 7. 8. 10:57
반응형

eplus를 사용하여 Excel 테이블의 행을 반복하려면 어떻게 해야 합니까?

저는 eplus가 처음이고, 엑셀 테이블에서 몇 가지 가치를 읽으려고 노력하고 있습니다.

지금까지 제가 가진 것은 다음과 같습니다.

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

하지만, 지금 저는 당황스럽습니다.ExcelTable가 있을 뿐입니다.Columns재산, 하지만 그렇지는 않습니다.Rows예상했던 대로의 재산찾을 수 없습니다.Rows라이브러리의 모든 개체에 대한 속성입니다.

행을 행으로 읽으며 표를 반복하려면 어떻게 해야 합니까?

동일한 문제에 대한 도움말을 검색하던 중 이 링크를 우연히 발견했습니다.그것은 확실히 저에게 효과가 있었습니다!Interop 객체를 사용하는 것보다 훨씬 낫습니다.:)

하지만 저는 그것을 약간 조정했습니다:

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

다음과 같이 전체 행을 가져올 수 있는 방법은 다음과 같습니다.ExcelRange이를 반복하거나 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
    // ...
}

액세스할 수 있습니다..Worksheet테이블의 속성 및 셀 색인을 지정합니다.이 목적을 위해 열 이름을 셀 값에 매핑하는 일련의 사전을 생성하는 확장 방법을 작성했습니다.

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

저도 같은 문제가 있었고 그것을 사용하여 해결했습니다.ExcelTable테이블 경계와 테이블 경계를 가져옵니다.ExcelWorksheet데이터를 검색합니다.따라서 코드는 다음과 같습니다.

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

표 머리글이나 다른 것들을 확인해야 하는데, 그게 저한테는 도움이 됐어요.

저는 또한 객체를 통해 적절하게 반복하고 이 API로 필요한 데이터를 얻는 방법을 찾고 있었습니다.

저는 다양한 게시물과 저자의 시작 페이지에서 정보를 수집하고 저와 다른 사람들을 돕기 위해 이 모든 것을 정리했습니다.

주요 문제는 반복을 위한 진입점입니다.제가 본 대부분의 솔루션은 워크시트를 따르고 있지만, 이 질문은 표에 구체적으로 나와 있습니다. 저는 두 가지 모두에 대해 궁금했기 때문에 두 가지 모두에 대한 제 연구 결과를 발표합니다.

워크시트 예제:

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

이제 여기서 일이 좀 꼬일 수 있습니다. 적어도 저에게는 처음부터 테이블이 없었습니다.동일한 패키지 사용 문에서 먼저 워크시트 셀 위에 반복한 다음 Tables 속성이 있는 모든 것을 터치하면 예외가 발생합니다.패키지를 다시 인스턴스화하고 동일한/유사한 코드를 사용하는 경우 테이블이 있는지 확인할 때 폭발하지 않습니다.

표 예:

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

기본적으로 모든 것이 동일한 방식으로 작동하고 작동합니다. 단지 아이를 돌봐야 합니다.부모, 별칭 표.워크시트를 사용하여 동일한 작업을 수행할 수 있습니다.다른 사람들이 언급했듯이 확장 방법과 래퍼 클래스를 사용하면 비즈니스 요구 사항의 세부 사항에 따라 보다 세분화할 수 있지만 이 질문의 목적은 아닙니다.

인덱싱 의견 및 응답과 관련하여 하드 코딩 인덱스 대 인덱싱되지 않은 기본 속성 대신 '행' 및 '열' 속성을 유지하는 것이 좋습니다. 적어도 새 버전에서는 문제가 없었습니다.

eplus는 잘 모르겠지만, 저는 LinkQToExcel을 사용하는 것에 대해 빠르게 제안할 것이라고 생각했습니다.

var excel = new ExcelQueryFactory(excel);

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

NuGet에서 얻을 수 있습니다.

Install-Package LinqToExcel

언급URL : https://stackoverflow.com/questions/21742038/how-do-i-iterate-through-rows-in-an-excel-table-using-epplus

반응형