在 EPPlus 中枚举范围时,是否总是按以下顺序检索单元格:先按行排序,然后按列排序?
When enumerating a range in EPPlus, are cells always retrieved in the following order : sorted by row, then by column?
假设我有以下代码:
using (ExcelPackage package = new ExcelPackage(...))
{
foreach(var cell in package.Workbook.Worksheets[1].Cells["A1:E5"])
{
//do something with "cell"
}
}
和以下 Excel 工作表:
A B C D E
1 foo bar . . .
2 . . . . .
3 . . hello . .
4 . . world . .
5 . . . . .
检索元素的顺序是否始终为:
foo => bar => hello => world ?
从我目前的尝试来看,这似乎总是正确的。
但这并不意味着总是如此。
似乎 Excel 在 .xlsx 文件中也按该顺序序列化单元格,即使单元格已按不同顺序编辑。
ExcelWorksheet class 中的文档并没有说太多:
//
// Summary:
// Provides access to a range of cells
public ExcelRange Cells { get; }
我回答我自己的问题。
TLTR:在当前的 EPPlus 版本 (v4.5.1) 中,给定范围的单元格始终按以下顺序枚举:先按行排序,然后按列排序。
我通过查看 EPPlus 发现了这一点 implementation。
给定工作表的单元格存储在 RangeCollection
对象中:
class ExcelWorksheet
{
RangeCollection _cells;
}
RangeCollection
包含一个单元格列表。此列表始终按 RangeID
排序。 RangeID 是行/列/工作表索引的组合。
这允许 EPPlus 通过执行二进制搜索快速找到单元格的索引(对于给定的行和列)。
class RangeCollection
{
List<IRangeID> _cells;
int IndexOf(ulong rangeID)
{
return Array.BinarySearch(...);
}
}
class ExcelCell : IRangeID
{
ulong RangeID
{
get
{
return GetCellID(_worksheet.SheetID, Row, Column);
}
}
ulong GetCellID(int SheetID, int row, int col)
{
return ((ulong)SheetID) + (((ulong)col) << 15) + (((ulong)row) << 29);
}
}
在枚举给定范围内的单元格时,EPPlus 将使用该排序列表来枚举范围内的单元格:
class ExcelRange
{
public bool MoveNext()
{
_index++;
//...
if (...)
{
GetStartIndexEnum(_fromRow, _fromCol, _toRow, _toCol);
//...
GetNextIndexEnum(_fromRow, _fromCol, _toRow, _toCol);
}
}
object IEnumerator.Current
{
get
{
return /*...*/ _worksheet._cells[_index] as ExcelCell /*...*/
}
}
}
GetStartIndexEnum()
和GetNextIndexEnum()
用于快速跳过当前枚举范围之外的单元格。按照与 RangeCollection 本身相同的顺序检查和枚举单元格,后者始终排序。
假设我有以下代码:
using (ExcelPackage package = new ExcelPackage(...))
{
foreach(var cell in package.Workbook.Worksheets[1].Cells["A1:E5"])
{
//do something with "cell"
}
}
和以下 Excel 工作表:
A B C D E
1 foo bar . . .
2 . . . . .
3 . . hello . .
4 . . world . .
5 . . . . .
检索元素的顺序是否始终为:
foo => bar => hello => world ?
从我目前的尝试来看,这似乎总是正确的。 但这并不意味着总是如此。
似乎 Excel 在 .xlsx 文件中也按该顺序序列化单元格,即使单元格已按不同顺序编辑。
ExcelWorksheet class 中的文档并没有说太多:
//
// Summary:
// Provides access to a range of cells
public ExcelRange Cells { get; }
我回答我自己的问题。
TLTR:在当前的 EPPlus 版本 (v4.5.1) 中,给定范围的单元格始终按以下顺序枚举:先按行排序,然后按列排序。
我通过查看 EPPlus 发现了这一点 implementation。
给定工作表的单元格存储在 RangeCollection
对象中:
class ExcelWorksheet
{
RangeCollection _cells;
}
RangeCollection
包含一个单元格列表。此列表始终按 RangeID
排序。 RangeID 是行/列/工作表索引的组合。
这允许 EPPlus 通过执行二进制搜索快速找到单元格的索引(对于给定的行和列)。
class RangeCollection
{
List<IRangeID> _cells;
int IndexOf(ulong rangeID)
{
return Array.BinarySearch(...);
}
}
class ExcelCell : IRangeID
{
ulong RangeID
{
get
{
return GetCellID(_worksheet.SheetID, Row, Column);
}
}
ulong GetCellID(int SheetID, int row, int col)
{
return ((ulong)SheetID) + (((ulong)col) << 15) + (((ulong)row) << 29);
}
}
在枚举给定范围内的单元格时,EPPlus 将使用该排序列表来枚举范围内的单元格:
class ExcelRange
{
public bool MoveNext()
{
_index++;
//...
if (...)
{
GetStartIndexEnum(_fromRow, _fromCol, _toRow, _toCol);
//...
GetNextIndexEnum(_fromRow, _fromCol, _toRow, _toCol);
}
}
object IEnumerator.Current
{
get
{
return /*...*/ _worksheet._cells[_index] as ExcelCell /*...*/
}
}
}
GetStartIndexEnum()
和GetNextIndexEnum()
用于快速跳过当前枚举范围之外的单元格。按照与 RangeCollection 本身相同的顺序检查和枚举单元格,后者始终排序。