我还试图弄清楚如何正确地遍历对象并使用此 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;
}
}
}
}
}
现在事情可能会有些混乱,至少对我来说,我没有桌子可以开始。在同一个包 using 语句下,如果我首先遍历工作表单元格,然后使用 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;
}
}
}
}
基本上一切工作和运作方式都相同,您只需要关注 child.Parent,AKA table.WorkSheet 即可获得相同的东西。正如其他人所提到的,扩展方法甚至可能的包装类可以根据您的业务需求的具体情况为您提供更多粒度,但这不是这个问题的目的。
关于索引 cmets 和响应,我建议坚持使用“行”和“列”属性,例如 first、last、for、foreach 等,而不是硬编码索引与非索引基本属性,至少在新版本的情况下,我在这里没有问题。