如何提高读取Excel的效率
当Excel中的数据量大,达到几十万条,使用Excel文件可能会变得很慢. 以下是一些建议来提高读取Excel的效率:
1. 选择合适的Excel库
在读取Excel文件时, 选择合适的Excel库可以显著提高效率. 一些常用的Excel库包括:
- EPPlus: 用于读写Excel文件的开源库
- NPOI: 用于读写Excel文件的开源库
- Microsoft.Office.Interop.Excel: Microsoft官方提供的Excel库
2. 使用单元格范围
在读取Excel数据时, 不要逐行读取, 而是使用单元格范围. 这种方式可以减少对Excel文件的读取次数, 从而提高读取效率.
以下是一个使用单元格范围的示例:
using (var package = new ExcelPackage(new FileInfo(filePath)))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[worksheetName];
var range = worksheet.Cells[2, 1, 50000, 20]; // 选择2-50000行, 1-20列的单元格范围
var data = range.Value; // 读取单元格范围中的数据
}
3. 使用多线程
使用多线程可以在读取Excel文件时提高效率. 例如, 可以使用一个线程读取Excel文件, 另一个线程处理数据.
以下是一个使用多线程的示例:
using (var package = new ExcelPackage(new FileInfo(filePath)))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[worksheetName];
var range = worksheet.Cells[2, 1, 50000, 20]; // 选择2-50000行, 1-20列的单元格范围
// 使用多线程读取单元格范围中的数据
var thread = new Thread(() => {
var data = range.Value; // 读取单元格范围中的数据
// 进行数据处理
});
thread.Start();
}
4. 关闭Excel应用程序
在读取Excel文件后, 要确保关闭Excel应用程序. 否则, Excel应用程序可能会保留在后台, 导致系统内存占用过高.
以下是一个关闭Excel应用程序的示例:
using (var package = new ExcelPackage(new FileInfo(filePath)))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[worksheetName];
var range = worksheet.Cells[2, 1, 50000, 20]; // 选择2-50000行, 1-20列的单元格范围
var data = range.Value; // 读取单元格范围中的数据
// 关闭Excel应用程序
package.Dispose();
}
以上是一些改善读取Excel效率的建议. 根据Excel文件的大小和数据结构, 可以选择不同的方法来提高读取效率.
以下是使用NPOI多线程读取Excel的实例:
using System.Threading.Tasks;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
// ...
public void ReadExcelWithMultipleThreads(string filePath, string worksheetName)
{
var workbook = new XSSFWorkbook(filePath);
var worksheet = workbook.GetSheet(worksheetName);
var range = new CellRangeAddress(1, worksheet.LastRowNum, 0, worksheet.GetRow(0).LastCellNum - 1); // 获取单元格范围
// 将单元格范围中的数据分块, 每块1000行
var chunks = range.Chunks(1000);
var tasks = new List<Task>();
foreach (var chunk in chunks)
{
var task = Task.Run(() =>
{
for (var i = chunk.FirstRow; i <= chunk.LastRow; i++)
{
var row = worksheet.GetRow(i);
if (row == null) continue;
for (var j = chunk.FirstColumn; j <= chunk.LastColumn; j++)
{
var cell = row.GetCell(j);
if (cell == null) continue;
var cellValue = cell.ToString();
// 处理单元格数据
}
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
workbook.Close();
}
在此示例中, 使用CellRangeAddress.Chunks()
方法将单元格范围分成多个块, 每个块包含1000行. 然后, 使用Task.Run()
方法将每个块的读取操作分配给一个线程. 最后, 使用Task.WaitAll()
等待所有线程完成后关闭Excel工作簿.
请注意, 在此示例中, 仅使用了一个工作表. 如果要同时读取多个工作表, 则需要根据需要进行修改.
CellRangeAddress.Chunks()
方法在NPOI的2.5.x版本及以上版本中可以使用。
CellRangeAddress
在NPOI中的命名空间为NPOI.SS.Util
。
感谢您的提醒,确实在NPOI的早期版本中没有CellRangeAddress.Chunks()
方法。如果您使用的是早期版本,可以考虑手动分块,或者升级NPOI到2.5.x及以上版本。在NPOI 2.5.x及以上版本中,CellRangeAddress.Chunks()
方法可以用于将单元格范围分成多个块,以便于在多线程环境下高效读取Excel文件。
另外,CellRangeAddress
的命名空间是NPOI.SS.Util
。
以下是手动实现分块的代码,达到Chunks()
方法的效果:
using System.Collections.Generic;
using NPOI.SS.Util;
// ...
public void ReadExcelManually(string filePath, string worksheetName)
{
var workbook = new XSSFWorkbook(filePath);
var worksheet = workbook.GetSheet(worksheetName);
var range = new CellRangeAddress(1, worksheet.LastRowNum, 0, worksheet.GetRow(0).LastCellNum - 1); // 获取单元格范围
var chunkSize = 1000; // 每个块的大小
var chunks = new List<CellRangeAddress>();
// 将单元格范围分成多个块, 每个块包含chunkSize行
for (var i = range.FirstRow; i <= range.LastRow; i += chunkSize)
{
var firstRow = i;
var lastRow = i + chunkSize - 1;
if (lastRow > range.LastRow) lastRow = range.LastRow;
chunks.Add(new CellRangeAddress(firstRow, lastRow, range.FirstColumn, range.LastColumn));
}
foreach (var chunk in chunks)
{
for (var i = chunk.FirstRow; i <= chunk.LastRow; i++)
{
var row = worksheet.GetRow(i);
if (row == null) continue;
for (var j = chunk.FirstColumn; j <= chunk.LastColumn; j++)
{
var cell = row.GetCell(j);
if (cell == null) continue;
var cellValue = cell.ToString();
// 处理单元格数据
}
}
}
workbook.Close();
}
在此示例中,我们手动将单元格范围分成多个块,每个块包含1000行,然后使用嵌套循环逐个读取单元格,并在每个单元格上执行必要的操作。需要注意的是,手动分块可能需要更多的代码,但是它可以在NPOI的早期版本中使用。
不过,可能需要根据自己的具体情况进行调整。如果你的数据结构与示例代码不同,你可能需要修改块的大小或循环的索引。