我的Excel文件大概有3000行,1000列。
我希望在这个海量数据中进行搜索,我尝试了使用POI,也尝试了比如先取出部分行数,如60行,并且在60行内进行搜索。但不论怎么样,我总是会遇到out of memroy的问题。
我的Excel文件大概有3000行,1000列。
我希望在这个海量数据中进行搜索,我尝试了使用POI,也尝试了比如先取出部分行数,如60行,并且在60行内进行搜索。但不论怎么样,我总是会遇到out of memroy的问题。
首先,处理超大的Excel文件时,遇到内存溢出(out of memory)问题是比较常见的。这是因为POI等库会将整个Excel文件加载到内存中,如果文件过大,就会占用大量内存。
如果你只需要读取前N行数据,可以考虑使用Apache的POI-OOXML
库,它支持按行读取Excel文件。这样,你只需要将数据一行行地读出,而不需要将整个文件加载到内存中。
下面是一个基本的代码示例:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
public class ReadExcel {
public static void main(String[] args) throws IOException {
// 文件路径
String filePath = "your_excel_file.xlsx";
FileInputStream fis = new FileInputStream(new File(filePath));
// 用WorkbookFactory创建Workbook对象
Workbook workbook = WorkbookFactory.create(fis);
// 得到第一页(sheet)
Sheet sheet = workbook.getSheetAt(0);
// 得到行迭代器
Iterator<Row> rowIterator = sheet.iterator();
int rowCount = 0;
while (rowIterator.hasNext() && rowCount < N) { // N为你想读取的行数
Row row = rowIterator.next();
// 这里你可以获取行中的列数据
// ...
rowCount++;
}
}
}
在这个代码中,我们首先打开了一个指向你的Excel文件的FileInputStream
。然后我们用WorkbookFactory
创建了一个Workbook
对象,得到了第一个sheet,然后通过迭代器一行行地读取数据。当达到你设定的行数N时,停止读取。注意,你需要替换你的Excel文件路径以及你想读取的行数N。
这个问题主要是因为POI的内存管理不太好,POI官方提供了SAX事件驱动模式,可以读取大文件。
EasyExcel也可以解决内存溢出的问题:
// 导入依赖包
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Sheet;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class ReadExcelDemo {
// 定义要读取的前N行数据的数量
private static final int ROW_COUNT = 1000;
public static void main(String[] args) {
// 定义要读取的Excel文件路径
String fileName = "path/to/excel/file.xlsx";
// 定义存储读取结果的列表
List<Object> dataList = new ArrayList<>();
// 使用EasyExcel读取Excel文件
EasyExcel.read(fileName, new AnalysisEventListener<Object>() {
@Override
public void invoke(Object data, AnalysisContext context) {
// 判断读取的行数是否超过了设定值
if (context.readRowHolder().getRowIndex() <= ROW_COUNT) {
// 将读取的数据存储到列表中
dataList.add(data);
} else {
// 超过设定值后停止读取
context.interrupt();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("Read {} rows of data from Excel file", context.readRowHolder().getRowIndex());
}
}).sheet().doRead();
// 处理读取结果
// ...
}
}
如果可以接受商业软件,可以考虑使用GcExcel。GcExcel 做了相关的处理,优化了内存的使用,不会出问题。同时它的API,使用起来比较简单,API是基于Range(区域),和Excel的概念相似,也有专门的API可以查找或者替换内容。
下面是个示例代码,我们从如下图的Excel文件中进行搜索:
代码:
public void FindText() {
// 构建 workbook
Workbook wb = new Workbook();
// 打开需要搜索的文件
wb.open("resources/ReplaceExample.xlsx");
// 打开对应的sheet
IWorksheet sheet = wb.getWorksheets().get(0);
FindOptions findOption = new FindOptions();
findOption.setMatchCase(true);
// 获取页面中包含内容的区域
IRange searchRange = sheet.getUsedRange();
// 存储查找到的区域
IRange range = null;
// 循环查找
do {
// 搜索包含++的格子,本例中会搜索出C++的格子区域
range = searchRange.find("++", findOption);
if (range != null) {
// 从查找到的区域中取出格子的值
String findVal = range.getValue().toString();
// 获取到值后,进行相关操作
}
}
while (range != null);
}
试试看用hutool的ExcelUtil.readBySax
添加一下maven:
覆盖实现一下里面的方法: