读写文件

您已经从架构中了解到,使用基本的 PhpSpreadsheet 类无法对持久性存储进行读写。为此目的,PhpSpreadsheet 提供了读取器和写入器,它们是\PhpOfficePhpSpreadsheet\Reader\IReader\PhpOffice\PhpSpreadsheet\Writer\IWriter 的实现。

\PhpOffice\PhpSpreadsheet\IOFactory

PhpSpreadsheet API 提供了多种方法来创建 PhpOffice\PhpSpreadsheet\Reader\IReader\PhpOfficePhpSpreadsheet\Writer\IWriter 实例:

通过 \PhpOffice\PhpSpreadsheet\IOFactory 直接创建。所有下面的示例都演示了直接创建方法。请注意,您还可以使用 \PhpOffice\PhpSpreadsheet\IOFactory 类来执行此操作。

使用 \PhpOffice\PhpSpreadsheet\IOFactory 来创建\PhpOffice\PhpSpreadsheet\Reader\IReader

有两种方法将文件读入 PhpSpreadsheet: 使用自动文件类型解析或显式指定。

自动文件类型解析会检查与 PhpSpreadsheet 一起分发的不同\PhpOffice\PhpSpreadsheet\Reader\IReader。如果其中一个可以加载指定的文件名,则使用该 \PhpOffice\PhpSpreadsheet\Reader\IReader 加载文件。显式模式需要您指定要使用的 \PhpOffice\PhpSpreadsheet\Reader\IReader

您可以使用以下代码示例在自动文件类型解析模式下使用\PhpOffice\PhpSpreadsheet\IOFactory 创建一个\PhpOffice\PhpSpreadsheet\Reader\IReader 实例:

$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load("05featuredemo.xlsx");

此功能的一个典型用法是,当您需要读取用户上传的文件,但您不知道它们是否上传了xls 或 xlsx 文件时。

如果您需要在读取器上设置某些属性(例如仅读取数据,稍后了解更多信息),则可能希望使用此变体:

  1. $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("05featuredemo.xlsx");
  2. $reader->setReadDataOnly(true);
  3. $reader->load("05featuredemo.xlsx");

以下代码示例演示了在显式模式下使用 \PhpOffice\PhpSpreadsheet\IOFactory 创建\PhpOfficePhpSpreadsheet\Reader\IReader 实例:

  1. $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
  2. $spreadsheet = $reader->load("05featuredemo.xlsx");

请注意,自动类型解析模式比显式模式略慢。

使用 \PhpOffice\PhpSpreadsheet\IOFactory 来创建 \PhpOffice\PhpSpreadsheet\Writer\IWriter

您可以使用 \PhpOffice\PhpSpreadsheet\IOFactory 创建\PhpOffice\PhpSpreadsheet\Writer\IWriter 实例:

  1. $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, "Xlsx");
  2. $writer->save("05featuredemo.xlsx");

Excel 2007 (SpreadsheetML)

Xlsx文件格式是 PhpSpreadsheet 的主要文件格式。它允许将内存中的电子表格输出到 .xlsx 文件中。

\PhpOffice\PhpSpreadsheet\Reader\Xlsx

读取 spreadsheet 你可以通过一下代码来读取 .xlsx 文件

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
  2. $spreadsheet = $reader->load("05featuredemo.xlsx");

只读数据

您可以在读取器上设置 setReadDataOnly 选项,以指示读取器忽略样式、数据验证等并仅读取单元格数据:

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
  2. $reader->setReadDataOnly(true);
  3. $spreadsheet = $reader->load("05featuredemo.xlsx");

读取指定 sheet 的数据

您可以在读取器上设置 setLoadSheetsOnly 选项,以指示读取器仅加载具有给定名称的工作表:

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
  2. $reader->setLoadSheetsOnly(["Sheet 1", "My special sheet"]);
  3. $spreadsheet = $reader->load("05featuredemo.xlsx");

获取指定单元格的数据

您可以在读取器上设置 setReadFilter 选项,以指示读取器仅加载与给定规则匹配的单元格。读取过滤器可以是实现 \PhpOfficePhpSpreadsheet\Reader\IReadFilter 接口的任何类。默认情况下,使用 \PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter 读取所有单元格。

以下代码将仅读取 Excel 文件中任何工作表的第 1 行和第 20-30行:

  1. class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
  2. public function readCell($columnAddress, $row, $worksheetName = '') {
  3. // Read title row and rows 20 - 30
  4. if ($row == 1 || ($row >= 20 && $row <= 30)) {
  5. return true;
  6. }
  7. return false;
  8. }
  9. }
  10. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
  11. $reader->setReadFilter( new MyReadFilter() );
  12. $spreadsheet = $reader->load("06largescale.xlsx");

读取过滤不会重新编号单元格行和列。如果您过滤仅读取行 100-200,您读取的单元格将仍然是 A100-A200,而不是 A1-A101。A1-A99 的单元格将不会被加载,但如果您然后尝试调用 getCell() 来获取已加载范围之外的单元格,那么 PHPSpreadsheet 将创建一个具有空值的新单元格。

诸如 toArray() 之类的方法假定电子表格中的所有单元格都已从 A1 加载,因此对于落在过滤器范围之外的行和列将返回null值:建议您跟踪过滤器请求的范围,并使用rangeToArray() 代替。

\PhpOffice\PhpSpreadsheet\Writer\Xlsx

写入 spreadsheet

您可以使用以下代码编写 .xlsx 文件:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
  2. $writer->save("05featuredemo.xlsx");

公式预计算

默认情况下,此写入器会预计算电子表格中的所有公式。对于大型电子表格而言,这可能会很慢,甚至可能是不必要的。但是,您可以禁用公式预计算:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
  2. $writer->setPreCalculateFormulas(false);
  3. $writer->save("05featuredemo.xlsx");

注意:即使将预计算设置为 false,任何被设置为自动调整大小的列中的公式仍然会被计算。

Office 2003 兼容套件

由于 Office2003 兼容性包中的错误,在打开 Xlsx 电子表格时可能会出现一些小问题(主要与公式计算有关)。您可以使用以下代码启用 Office2003 兼容性:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
  2. $writer->setOffice2003Compatibility(true);
  3. $writer->save("05featuredemo.xlsx");

Office2003 兼容性选项仅在需要时使用,因为它会禁用几个 Office2007 文件格式选项,从而导致功能较低的 Office2007 电子表格。

表单控件字段

PhpSpreadsheet 对表单控件(按钮、复选框等)的支持有限。仅支持 Excel 2007 格式,并且仅提供此功能以允许加载带有此类控件的电子表格并将其保存为新文件。不支持将此类元素添加到电子表格中,甚至无法定位它们以确定它们的属性(因此您无法修改或删除它们)。对带有控件的工作表进行更改是“买者自负”;有些更改会正确工作,但其他更改很可能会导致问题,例如在工作表中添加注释,或以可能导致控件位置更改的方式插入或删除行或列。

Excel 5 (BIFF) 格式

Xls文件格式是旧版的 Excel 文件格式,在 PhpSpreadsheet 中实现以提供创建 .xlsx和 .xls 文件的统一方式。它基本上是 PEAR Spreadsheet_Excel_Writer 的修改版本,虽然已经被扩展并且比旧版PEAR库具有更少的限制和更多的功能。这可以读取所有使用OLE2 的 BIFF 版本:从 BIFF 5 (随 Office 95 一起引入)到 BIFF8,但无法读取早期版本。

Xls文件格式将不再进一步开发,只提供PhpSpreadsheet的附加文件格式。

关于 Excel5 (BIFF)的限制请注意,BIFF文件格式在通过PHP处理大型电子表格时的单元格样式方面存在一些限制。

\PhpOffice\PhpSpreadsheet\Reader\Xls

读取 spreadsheet

通过一下代码读取 xls 文件

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
  2. $spreadsheet = $reader->load("05featuredemo.xls");

只读数据

您可以在读取器上设置 setReadDataOnly 选项,以指示读取器仅读取单元格数据,忽略样式、数据验证等。默认情况下,所有单元格都使用\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter 进行读取。

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
  2. $reader->setReadDataOnly(true);
  3. $spreadsheet = $reader->load("05featuredemo.xls");

读取指定 sheets

您可以在读取器上设置 setLoadSheetsOnly 选项,以指示读取器仅加载具有给定名称的工作表。

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
  2. $reader->setLoadSheetsOnly(["Sheet 1", "My special sheet"]);
  3. $spreadsheet = $reader->load("05featuredemo.xls");

读取指定单元格的数据

您可以在读取器上设置 setReadFilter 选项,以指示读取器仅加载与给定规则匹配的单元格。阅读过滤器可以是任何实现 \PhpOffice\PhpSpreadsheet\Reader\IReadFilter 接口的类。默认情况下,所有单元格都使用\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter 进行读取。

以下代码将仅读取 Excel 文件中任何工作表的第 1 行和第 20 到 30 行:

  1. class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
  2. public function readCell($columnAddress, $row, $worksheetName = '') {
  3. // Read title row and rows 20 - 30
  4. if ($row == 1 || ($row >= 20 && $row <= 30)) {
  5. return true;
  6. }
  7. return false;
  8. }
  9. }
  10. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
  11. $reader->setReadFilter( new MyReadFilter() );
  12. $spreadsheet = $reader->load("06largescale.xls");

\PhpOffice\PhpSpreadsheet\Writer\Xls

写入 spreadsheet

通过以下代码写入 .xls 文件

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet);
  2. $writer->save("05featuredemo.xls");

Excel 2003 XML 文件格式

Excel 2003 XML 文件格式是一种可在旧版 Microsoft Excel 中使用的文件格式。

Excel 2003 XML 的限制请注意,Excel 2003 XML 格式在通过 PHP 处理大型电子表格时的单元格样式方面存在一些限制。此外,仅支持使用 UTF-8 字符集的文件。

\PhpOffice\PhpSpreadsheet\Reader\Xml

读取数据

您可以使用以下代码读取 Excel 2003 的 .xml 文件:

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml();
  2. $spreadsheet = $reader->load("05featuredemo.xml");

读取指定单元格的数据

您可以在读取器上设置 setReadFilter 选项,以指示读取器仅加载与给定规则匹配的单元格。阅读过滤器可以是任何实现 \PhpOffice\PhpSpreadsheet\Reader\IReadFilter 接口的类。默认情况下,所有单元格都使用\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter 进行读取。

以下代码将仅读取 Excel 文件中任何工作表的第 1 行和第 20 到 30 行:

  1. class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
  2. public function readCell($columnAddress, $row, $worksheetName = '') {
  3. // Read title row and rows 20 - 30
  4. if ($row == 1 || ($row >= 20 && $row <= 30)) {
  5. return true;
  6. }
  7. return false;
  8. }
  9. }
  10. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml();
  11. $reader->setReadFilter( new MyReadFilter() );
  12. $spreadsheet = $reader->load("06largescale.xml");

CSV 文件

CSV (逗号分隔值)通常用于与其他系统进行导入/导出文件格式。PhpSpreadsheet 允许读取和写入 CSV 文件。

CSV 的限制请注意,CSV 文件格式在单元格样式、数字格式化等方面存在一些限制。

\PhpOffice\PhpSpreadsheet\Reader\Csv

读取 CSV 文件

通过以下代码读取 CSV 文件

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $spreadsheet = $reader->load('sample.csv');

您还可以将字符串视为 CSV 文件的内容,如下所示:

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $spreadsheet = $reader->loadSpreadsheetFromString($data);

设置 CSV 选项

通常,CSV 文件并不是真正的逗号分隔,或者使用分号(;)作为分隔符。在读取CSV文件之前,您可以设置一些选项。

分隔符将自动检测,因此在大多数情况下不需要指定它。但是,如果自动检测不符合用例,则可以手动设置。

请注意,默认情况下 \PhpOffice\PhpSpreadsheet\Reader\Csv 假定加载的 CSV 文件采用 UTF-8 编码。如果您正在读取在 Microsoft Office Exce l中创建的 CSV 文件,则正确的输入编码可能是 Windows-1252(CP1252)。始终确保正确设置输入编码。

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $reader->setInputEncoding('CP1252');
  3. $reader->setDelimiter(';');
  4. $reader->setEnclosure('');
  5. $reader->setSheetIndex(0);
  6. $spreadsheet = $reader->load("sample.csv");

您还可以让 PhpSpreadsheet 尝试猜测输入编码。它将根据测试 BOM(UTF-8、UTF-16BE、UTF-16LE、UTF-32BE或UTF-32LE) 来进行,或者通过对这些编码进行启发式测试来实现,如果所有这些测试都失败了,则回退到指定的编码(默认为 CP1252)。

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding('sample.csv');
  3. // or, e.g. $encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding(
  4. // 'sample.csv', 'ISO-8859-2');
  5. $reader->setInputEncoding($encoding);
  6. $reader->setDelimiter(';');
  7. $reader->setEnclosure('');
  8. $reader->setSheetIndex(0);
  9. $spreadsheet = $reader->load('sample.csv');

您还可以将读取器设置为猜测编码,而不是直接调用 guessEncoding。在这种情况下,如果没有其他方法可用,则使用用户可设置的回退编码。

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $reader->setInputEncoding(\PhpOffice\PhpSpreadsheet\Reader\Csv::GUESS_ENCODING);
  3. $reader->setFallbackEncoding('ISO-8859-2'); // default CP1252 without this statement
  4. $reader->setDelimiter(';');
  5. $reader->setEnclosure('');
  6. $reader->setSheetIndex(0);
  7. $spreadsheet = $reader->load('sample.csv');

CSV 阅读器通常不会将空字符串加载到电子表格中。要加载它们,请执行以下操作:

$reader->setPreserveNullString(true);

最后,您可以通过 new Csv() 或 IOFactory::load 来调用构造函数时设置回调函数,并让该回调函数将可自定义属性设置为适合您环境的默认值。

  1. function constructorCallback(\PhpOffice\PhpSpreadsheet\Reader\Csv $reader): void
  2. {
  3. $reader->setInputEncoding(\PhpOffice\PhpSpreadsheet\Reader\Csv::GUESS_ENCODING);
  4. $reader->setFallbackEncoding('ISO-8859-2');
  5. $reader->setDelimiter(',');
  6. $reader->setEnclosure('"');
  7. // Following represents how Excel behaves better than the default escape character
  8. $reader->setEscapeCharacter((version_compare(PHP_VERSION, '7.4') < 0) ? "\x0" : '');
  9. }
  10. \PhpOffice\PhpSpreadsheet\Reader\Csv::setConstructorCallback('constructorCallback');
  11. $spreadsheet = \PhpSpreadsheet\IOFactory::load('sample.csv');

读取指定工作表的数据

CSV 文件只能包含一个工作表。因此,您可以指定从 CSV 中读取哪个工作表:

$reader->setSheetIndex(0);

读取数据到一个已经存在的 spreadsheet

在处理 CSV 文件时,您可能希望将 CSV 数据导入现有的电子表格对象。以下代码将 CSV文件加载到包含一些工作表的现有 $spreadsheet 中,并将其导入到第 6 个工作表上:

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $reader->setDelimiter(';');
  3. $reader->setEnclosure('"');
  4. $reader->setSheetIndex(5);
  5. $reader->loadIntoExisting("05featuredemo.csv", $spreadsheet);

行结束符

Unix(\n)和Windows(\r\n)的行结束符都支持。

只要 PHP 本身支持它们,Mac行结束符(\r)也受支持,这在 PHP 8.0 版本中是可行的。对于 PHP 8.1 版本,对 Mac 行结束符的支持已被废弃,并计划在未来所有后续的PHP 8 版本中继续被废弃;PhpSpreadsheet 将继续为 8.* 版本提供对它们的支持。计划在9版本中删除对它们的支持;然后 PhpSpreadsheet 将不再正确处理使用 Mac 行结束符的 CSV 文件。

您可以通过以下方式抑制对 Mac 行结束符的测试:

  1. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
  2. $reader->setTestAutoDetect(false);

\PhpOffice\PhpSpreadsheet\Writer\Csv

写入 CSV 文件

您可以使用以下代码来写入一个 .csv 文件:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
  2. $writer->save("05featuredemo.csv");
设置 CSV 选项

通常,CSV 文件并不是真正的逗号分隔,或者使用分号 (;) 作为分隔符。在写入 CSV 文件之前,您可以设置一些选项:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
  2. $writer->setDelimiter(';');
  3. $writer->setEnclosure('"');
  4. $writer->setLineEnding("\r\n");
  5. $writer->setSheetIndex(0);
  6. $writer->save("05featuredemo.csv");
CSV 封装

默认情况下,所有 CSV 字段都用封装字符括起来,该字符默认为双引号。只有在需要时,您才可以更改为仅使用封装字符:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
  2. $writer->setEnclosureRequired(false);
  3. $writer->save("05featuredemo.csv");
写入指定工作表

CSV 文件只能包含一个工作表。因此,您可以指定要写入 CSV 的工作表:

$writer->setSheetIndex(0);

公式预计算

默认情况下,此写入器会预计算电子表格中的所有公式。这在大型电子表格上可能会很慢,甚至可能是不需要的。但是,您可以禁用公式预计算:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
  2. $writer->setPreCalculateFormulas(false);
  3. $writer->save("05featuredemo.csv");
写入 UTF-8 CSV 文件

CSV 文件以 UTF-8 编码写入。如果它们不包含 ASCII 范围之外的字符,则无需执行其他操作。但是,如果文件中存在这样的字符,或者文件以两个字符 “ID” 开头,则应明确包含 BOM 文件头;如果没有,Excel 将无法正确解释这些字符。可以使用以下代码启用此功能:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
  2. $writer->setUseBOM(true);
  3. $writer->save("05featuredemo.csv");
使用期望的编码写入 CSV 文件

它可以设置为以 PHP 的 mb_convert_encoding 指定的编码输出。这看起来像以下代码:

  1. $writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
  2. $writer->setUseBOM(false);
  3. $writer->setOutputEncoding('SJIS-WIN');
  4. $writer->save("05featuredemo.csv");
小数和千位分隔符

如果您要导出的电子表格包含带小数或千位分隔符的数字,则在执行导出之前,应考虑使用哪些字符。

默认情况下,PhpSpreadsheet 会根据服务器的区域设置来决定使用哪些字符。但是为了避免问题,建议显式设置字符,如下所示:

英语用户在执行导出之前应使用以下内容:

  1. \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator('.');
  2. \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator(',');

德语用户应使用相反的值。

  1. \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator(',');
  2. \PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator('.');

注意,上述代码将小数和千位分隔符设置为全局选项。这也会影响 HTML 和 PDF 的导出方式。