使用 PHPExcel 只读取一个大 Excel 文件的一行
Read only one row of a big Excel file with PHPExcel
我正在使用 PHPExcel 库从 Excel 文件中读取数据。我的文件大约 5mb,70 列和 20000 行。加载文件的代码是:
$sheetnames = array('Classification');
$excelFile = Yii::app()->basePath . '/categories/'. $region .'.xlsx';
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadDataOnly(true);
$objReader->setLoadSheetsOnly($sheetnames);
$objPHPExcel = $objReader->load($excelFile);
Excel 文件具有以下结构:
Title | Id | Path | Attribute 1 | Attribute 2 | ... | Attribute 65
加载此文件大约需要 6 分钟,占用太多 CPU 和 RAM。
实际上,我只需要知道具有给定 ID 的一行数据。现在我遍历所有行并检查 id。效率太低了。
所以我有两个问题:
- 有没有更快加载文件的方法? (我用不起这么多时间,cpu和ram)
- 有没有更有效地搜索文件的方法?
处理exel文件有点困难。只需使用 shell exec 将它们转换为 CSV,并根据需要对这些 CSV 文件执行任何操作。
$ easy_install xlsx2csv
$ xlsx2csv file.xlsx newfile.csv
转换时间不到一秒。
首先使用读取过滤器仅加载 ID 列:
/** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */
class SingleColumnFilter implements PHPExcel_Reader_IReadFilter
{
private $requestedColumn;
public function __construct($column) {
$this->requestedColumn = $column;
}
public function readCell($column, $row, $worksheetName = '') {
if ($column == $this->requestedColumn) {
return true;
}
return false;
}
}
/** Create an Instance of our Read Filter **/
$idColumnFilter = new SingleColumnFilter('B'); // Id is column B
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadDataOnly(true);
$objReader->setLoadSheetsOnly($sheetnames);
/** Tell the Reader that we want to use the Read Filter **/
$objReader->setReadFilter($idColumnFilter);
/** Load only the column that matches our filter to PHPExcel **/
$objPHPExcel = $objReader->load($inputFileName);
然后 PHPExcel 将只加载列 B
中单元格的数据。然后,您可以通过该单元格子集搜索所需的值(1 列和 22,000 行只是 22,000 个单元格,因此应该比加载整个文件所需的 2.5GB 更接近 35MB),然后使用类似的根据行号过滤以仅加载您已识别的单行。
编辑
PHPExcel 的最新 1.8.1 版本也有一个 columnIterator,它应该可以更容易地向下迭代查找特定 ID 值的列:
$found = false;
foreach ($objPHPExcel->getActiveSheet()->getColumnIterator('B') as $column) {
$cellIterator = $column->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $key => $cell) {
if ($cell->getValue == 'ABC') {
$found = true;
$rowId = $cell->getRow()
break 2;
}
}
编辑 #2
确定所需的行后,您可以使用第二个过滤器重新加载 Excel 文件...但只有那一行:
/** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */
class SingleRowFilter implements PHPExcel_Reader_IReadFilter
{
private $requestedRow;
public function __construct($row) {
$this->requestedRow = $row;
}
public function readCell($column, $row, $worksheetName = '') {
if ($row == $this->requestedRow) {
return true;
}
return false;
}
}
if ($found) {
/** Create an Instance of our Read Filter **/
$rowFilter = new SingleRowFilter($rowId);
$objReader2 = PHPExcel_IOFactory::createReader('Excel2007');
$objReader2->setReadDataOnly(true);
$objReader2->setLoadSheetsOnly($sheetnames);
/** Tell the Reader that we want to use the Read Filter **/
$objReader2->setReadFilter($rowFilter);
/** Load only the single row that matches our filter to PHPExcel **/
$objPHPExcel2 = $objReader2->load($inputFileName);
}
如果你想加速你的程序并减少内存消耗,你可以看看 Spout:https://github.com/box/spout。
您只需:
$reader = ReaderFactory::create(Type::CSV);
$reader->open($filePath);
while ($reader->hasNextRow()) {
$row = $reader->nextRow();
$id = $row[1];
// do stuff with the $id
}
$reader->close();
浏览整个文件需要 1 到 2 秒:)
我正在使用 PHPExcel 库从 Excel 文件中读取数据。我的文件大约 5mb,70 列和 20000 行。加载文件的代码是:
$sheetnames = array('Classification');
$excelFile = Yii::app()->basePath . '/categories/'. $region .'.xlsx';
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadDataOnly(true);
$objReader->setLoadSheetsOnly($sheetnames);
$objPHPExcel = $objReader->load($excelFile);
Excel 文件具有以下结构:
Title | Id | Path | Attribute 1 | Attribute 2 | ... | Attribute 65
加载此文件大约需要 6 分钟,占用太多 CPU 和 RAM。 实际上,我只需要知道具有给定 ID 的一行数据。现在我遍历所有行并检查 id。效率太低了。
所以我有两个问题:
- 有没有更快加载文件的方法? (我用不起这么多时间,cpu和ram)
- 有没有更有效地搜索文件的方法?
处理exel文件有点困难。只需使用 shell exec 将它们转换为 CSV,并根据需要对这些 CSV 文件执行任何操作。
$ easy_install xlsx2csv
$ xlsx2csv file.xlsx newfile.csv
转换时间不到一秒。
首先使用读取过滤器仅加载 ID 列:
/** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */
class SingleColumnFilter implements PHPExcel_Reader_IReadFilter
{
private $requestedColumn;
public function __construct($column) {
$this->requestedColumn = $column;
}
public function readCell($column, $row, $worksheetName = '') {
if ($column == $this->requestedColumn) {
return true;
}
return false;
}
}
/** Create an Instance of our Read Filter **/
$idColumnFilter = new SingleColumnFilter('B'); // Id is column B
$objReader = PHPExcel_IOFactory::createReader('Excel2007');
$objReader->setReadDataOnly(true);
$objReader->setLoadSheetsOnly($sheetnames);
/** Tell the Reader that we want to use the Read Filter **/
$objReader->setReadFilter($idColumnFilter);
/** Load only the column that matches our filter to PHPExcel **/
$objPHPExcel = $objReader->load($inputFileName);
然后 PHPExcel 将只加载列 B
中单元格的数据。然后,您可以通过该单元格子集搜索所需的值(1 列和 22,000 行只是 22,000 个单元格,因此应该比加载整个文件所需的 2.5GB 更接近 35MB),然后使用类似的根据行号过滤以仅加载您已识别的单行。
编辑
PHPExcel 的最新 1.8.1 版本也有一个 columnIterator,它应该可以更容易地向下迭代查找特定 ID 值的列:
$found = false;
foreach ($objPHPExcel->getActiveSheet()->getColumnIterator('B') as $column) {
$cellIterator = $column->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $key => $cell) {
if ($cell->getValue == 'ABC') {
$found = true;
$rowId = $cell->getRow()
break 2;
}
}
编辑 #2
确定所需的行后,您可以使用第二个过滤器重新加载 Excel 文件...但只有那一行:
/** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */
class SingleRowFilter implements PHPExcel_Reader_IReadFilter
{
private $requestedRow;
public function __construct($row) {
$this->requestedRow = $row;
}
public function readCell($column, $row, $worksheetName = '') {
if ($row == $this->requestedRow) {
return true;
}
return false;
}
}
if ($found) {
/** Create an Instance of our Read Filter **/
$rowFilter = new SingleRowFilter($rowId);
$objReader2 = PHPExcel_IOFactory::createReader('Excel2007');
$objReader2->setReadDataOnly(true);
$objReader2->setLoadSheetsOnly($sheetnames);
/** Tell the Reader that we want to use the Read Filter **/
$objReader2->setReadFilter($rowFilter);
/** Load only the single row that matches our filter to PHPExcel **/
$objPHPExcel2 = $objReader2->load($inputFileName);
}
如果你想加速你的程序并减少内存消耗,你可以看看 Spout:https://github.com/box/spout。
您只需:
$reader = ReaderFactory::create(Type::CSV);
$reader->open($filePath);
while ($reader->hasNextRow()) {
$row = $reader->nextRow();
$id = $row[1];
// do stuff with the $id
}
$reader->close();
浏览整个文件需要 1 到 2 秒:)