原创开发运维 本文章已被<常用Composer包经典推荐>专题收录

PHP实现读写Excel文件的经典Composer包phpoffice/phpspreadsheet使用详记

本文阅读 20 分钟
首页 开发运维 正文 第2页

调用方法(参考于官方文档)

Hello World 一个简单生成表格示例:

<?php

require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$spreadsheet = new Spreadsheet();
$activeWorksheet = $spreadsheet->getActiveSheet();
$activeWorksheet->setCellValue('A1', 'Hello World !');

$writer = new Xlsx($spreadsheet);
$writer->save('hello world.xlsx');

入门的好方法是参考一些样例。不要忘记通过-Prefer-Source Composer标志下载它们。然后通过PHP内置网络服务器为其提供服务:

php -S localhost:8000 -t vendor/phpoffice/phpspreadsheet/samples

然后,把你的浏览器指向:

http://localhost:8000/

示例也可以直接从命令行运行,例如:

php vendor/phpoffice/phpspreadsheet/samples/Basic/01_Simple.php

访问表格和单元格

1.获取工作薄 getActiveSheet

<?php
// 引入composer自动加载文件
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
// 获取Spreadsheet对象(电子表格对象)
$spreadsheet = new Spreadsheet;
// 获取活动工作薄(工作薄对象)
$sheet = $spreadsheet->getActiveSheet();

2.获取单元格对象的两种方式

// $sheet 是工作薄对象
// 获取单元格的两种方式(返回单元格对象)
// getCell(坐标) 示例: getCell('A2')
// getCellByColumnAndRow(列数,行数) 示例: getCellByColumnAndRow(1, 2)
$test1 = $sheet->getCell('A2');
// 第一列第一行
$test2 = $sheet->getCellByColumnAndRow(1, 2);

var_dump($test1);
var_dump($test2);

3.获取单元格的值getValue()和坐标getCoordinate()

getValue() - 获取单元格的值
// getValue() 获取单元格的值
$cell = $sheet->getCell('A2');
$cellValue = $cell->getValue();
var_dump($cellValue);

getCoordinate() - 获取单元格的坐标
// getCoordinate() 获取单元格坐标
$cell = $sheet->getCell('A2');
$coordinate = $cell->getCoordinate();
var_dump($coordinate);//'A2'

4.将表格保存在服务器上

<?php

// 引入composer自动加载文件
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

// 获取Spreadsheet对象(电子表格对象)
$spreadsheet = new Spreadsheet;

// 获取活动工作薄(工作薄对象)
$sheet = $spreadsheet->getActiveSheet();
// 设置单元格的值
$sheet->getCell('A1')->setValue('123');

// 表格保存在服务器上
$writer = new Xlsx($spreadsheet);
$writer->save('1.xlsx');

按坐标设置单元格值

通过坐标设置单元格值可以使用工作表的setCellValue()方法完成。

// 设置字符串为A1单元格值
$spreadsheet->getActiveSheet()->setCellValue('A1', 'PhpSpreadsheet');

// 设置数字为A2单元格值
$spreadsheet->getActiveSheet()->setCellValue('A2', 12345.6789);

// 设置布尔值为A3单元格值
$spreadsheet->getActiveSheet()->setCellValue('A3', TRUE);

// 设置公式为A4单元格值
$spreadsheet->getActiveSheet()->setCellValue(
    'A4',
    '=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))'
);

或者,您可以检索单元格对象,然后调用单元格的setValue()方法

$spreadsheet->getActiveSheet()
    ->getCell('B8')
    ->setValue('要设置的值');

或者,通过setCellValueByColumnAndRow指定列数行数来设置单元格的值。
通过工作薄对象直接调用,返回工作薄对象

$sheet->setCellValueByColumnAndRow(1, 1, '张三');

创建新单元格

如果调用getCell(),而单元格不存在,则PhpSpreadsheet将为您创建该单元格。

可以考虑以下代码:

$spreadSheet = new Spreadsheet();
$workSheet = $spreadSheet->getActiveSheet();
// Set details for the formula that we want to evaluate, together with any data on which it depends
$workSheet->fromArray(
    [1, 2, 3],
    null,
    'A1'
);

$cellC1 = $workSheet->getCell('C1');
echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;

$cellA1 = $workSheet->getCell('A1');
echo 'Value: ', $cellA1->getValue(), '; Address: ', $cellA1->getCoordinate(), PHP_EOL;

echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;

呼叫GetCell('C1')返回包含其值(3)的C1的单元格,以及其链接到集合(用于识别其地址/坐标C1)。随后的访问单元格A1的调用将修改$cellc1的值,从而将其链接脱离到集合。
因此,当我们尝试显示该值并第二次地址时,我们可以显示其值,但是尝试显示其地址/坐标会引发异常,因为该链接已设置为null。
注意:有一些内部方法可以从集合中获取其他单元格,这也将从您可能分配给变量的任何单元格中分离到集合的链接。

Excel 数据类型

MS Excel 支持 7 种基本数据类型:

  • string
  • number
  • boolean
  • null
  • formula
  • error
  • Inline (or rich text) string

默认情况下,当您调用工作表的setCellValue()方法或单元格的setValue()方法时,PhpSpreadsheet 将为 PHP 空值、布尔值、浮点数或整数使用适当的数据类型;或将传递给该方法的任何字符串数据值转换为最合适的数据类型,因此数字字符串将转换为数字,而以开头的字符串值经=将转换为公式。非数字或不以前导开头的字符串=将被视为真正的字符串值。

请注意,以前导零开头的数字字符串(没有紧跟小数点分隔符)不会转换为数字,因此电话号码等值(例如“01615991375”将保留为字符串)。

此“转换”由单元格“值绑定器”处理,您可以编写自定义“值绑定器”来更改这些“转换”的行为。标准的 PhpSpreadsheet 包还提供了一个“高级值绑定器”,可以处理许多更复杂的转换,例如将具有小数格式(如“3/4”)的字符串转换为数字值(在本例中为 0.75)并设置适当的“分数”数字格式掩码。同样,像“5%”这样的字符串将被转换为 0.05 的值,并应用百分比数字格式掩码,包含看起来像日期的值的字符串将被转换为 Excel 序列化的日期时间戳值,并应用相应的掩码。这在从 csv 文件加载数据时特别有用,

高级值绑定器处理的格式包括:

  • TRUE 或 FALSE(取决于区域设置)被转换为布尔值。
  • 标识为科学(指数)格式的数字字符串将转换为数字。
  • 分数和普通分数被转换为数字,并应用适当的数字格式掩码。
  • 百分比被转换为数字,除以 100,并应用适当的数字格式掩码。
  • 日期和时间转换为 Excel 时间戳值(数字),并应用适当的数字格式掩码。
  • 当字符串包含换行符 ( \n) 时,单元格样式设置为换行。

基本上,它试图模仿 MS Excel GUI 的行为。

在单元格中设置公式

如上所述,如果您= 在单元格中存储第一个字符为 an 的字符串值。PHPSpreadsheet 将该值视为公式,然后您可以通过调用getCalculatedValue()单元格来计算该公式。
但是,有时您希望将以=开头的值存储为字符串,并且您不希望 PHPSpreadsheet 将其计算为公式。
为此,您需要通过将其设置为“引用文本”来“转义”该值。

// Set cell A4 with a formula
$spreadsheet->getActiveSheet()->setCellValue(
    'A4',
    '=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))'
);
$spreadsheet->getActiveSheet()->getCell('A4')
    ->getStyle()->setQuotePrefix(true);

然后,即使您要求 PHPSpreadsheet 返回单元格的计算值 A4,它也会=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1)) 作为字符串返回,而不是尝试计算公式。

在单元格中设置日期 和/或 时间值

日期或时间值在 Excel 中作为时间戳保存(一个简单的浮点值),数字格式掩码用于显示该值的格式;所以如果我们想在单元格中存储日期,我们需要计算正确的 Excel 时间戳,并设置数字格式掩码。

// Get the current date/time and convert to an Excel date/time
$dateTimeNow = time();
$excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $dateTimeNow );
// Set cell A6 with the Excel date/time value
$spreadsheet->getActiveSheet()->setCellValue(
    'A6',
    $excelDateValue
);
// Set the number format mask so that the excel timestamp will be displayed as a human-readable date/time
$spreadsheet->getActiveSheet()->getStyle('A6')
    ->getNumberFormat()
    ->setFormatCode(
        \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
    );

设置一个带前导零的数字

默认情况下,PhpSpreadsheet 会自动检测值类型并将其设置为适当的 Excel 数字数据类型。此类型转换由值绑定器处理,如本文档标题为“使用值绑定器促进数据输入”的部分所述。

数字没有前导零,因此如果您尝试设置一个确实有前导零的数值(例如电话号码),那么这些值通常会丢失,因为该值被转换为数字,因此“01513789642”将显示为 1513789642。

有两种方法可以强制 PhpSpreadsheet 覆盖此行为。

首先,您可以将数据类型显式设置为字符串,这样它就不会转换为数字。

// Set cell A8 with a numeric value, but tell PhpSpreadsheet it should be treated as a string
$spreadsheet->getActiveSheet()->setCellValueExplicit(
    'A8',
    "01513789642",
    \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING
);

或者,您可以使用数字格式掩码来显示带前导零的值。

// Set cell A9 with a numeric value
$spreadsheet->getActiveSheet()->setCellValue('A9', 1513789642);
// Set a number format mask to display the value as 11 digits with leading zeroes
$spreadsheet->getActiveSheet()->getStyle('A9')
    ->getNumberFormat()
    ->setFormatCode(
        '00000000000'
    );

使用数字格式屏蔽,您甚至可以将数字分成几组,使值更易于阅读。

// Set cell A10 with a numeric value
$spreadsheet->getActiveSheet()->setCellValue('A10', 1513789642);
// Set a number format mask to display the value as 11 digits with leading zeroes
$spreadsheet->getActiveSheet()->getStyle('A10')
    ->getNumberFormat()
    ->setFormatCode(
        '0000-000-0000'
    );

07-simple-example-1.webp

注意:并非所有复杂的格式掩码(如这个)在检索格式化值以显示“屏幕上”时都适用,或者对于某些编写器(如 HTML 或 PDF),但它适用于真正的电子表格编写器(Xlsx 和 Xls) .

设置数组中的单元格范围

也可以通过将值数组传递给该方法,在一次调用中设置一系列单元格值fromArray()

$arrayData = [
    [NULL, 2010, 2011, 2012],
    ['Q1',   12,   15,   21],
    ['Q2',   56,   73,   86],
    ['Q3',   52,   61,   69],
    ['Q4',   30,   32,    0],
];
$spreadsheet->getActiveSheet()
    ->fromArray(
        $arrayData,  // The data to set
        NULL,        // Array values with this value will not be set
        'C3'         // Top left coordinate of the worksheet range where
                     //    we want to set these values (default is A1)
    );

07-simple-example-2.webp

如果你传递一个二维数组,那么这将被视为一系列的行和列。一维数组将被视为单行,如果您要从数据库中获取数据数组,这将特别有用。

$rowArray = ['Value1', 'Value2', 'Value3', 'Value4'];
$spreadsheet->getActiveSheet()
    ->fromArray(
        $rowArray,   // The data to set
        NULL,        // Array values with this value will not be set
        'C3'         // Top left coordinate of the worksheet range where
                     //    we want to set these values (default is A1)
    );

07-simple-example-3.webp

如果你有一个简单的一维数组,并想将它写成一列,那么下面的代码会将它转换成一个适当结构化的二维数组,可以将其提供给方法fromArray()

$rowArray = ['Value1', 'Value2', 'Value3', 'Value4'];
$columnArray = array_chunk($rowArray, 1);
$spreadsheet->getActiveSheet()
    ->fromArray(
        $columnArray,   // The data to set
        NULL,           // Array values with this value will not be set
        'C3'            // Top left coordinate of the worksheet range where
                        //    we want to set these values (default is A1)
    );

07-simple-example-4.webp

按坐标检索单元格值

要检索单元格的值,应首先使用 方法从工作表中检索单元格getCell()。可以使用该方法读取单元格的值getValue()

// Get the value from cell A1
$cellValue = $spreadsheet->getActiveSheet()->getCell('A1')->getValue();

这将检索包含在单元格中的原始、未格式化的值。

如果单元格包含公式,并且您需要检索计算值而不是公式本身,则使用单元格的 getCalculatedValue()方法。这在计算引擎中有进一步解释 。

// Get the value from cell A4
$cellValue = $spreadsheet->getActiveSheet()->getCell('A4')->getCalculatedValue();

或者,如果您想查看应用了任何单元格格式的值(例如,对于人类可读的日期或时间值),则可以使用单元格的方法getFormattedValue()

// Get the value from cell A6
$cellValue = $spreadsheet->getActiveSheet()->getCell('A6')->getFormattedValue();

按列和行设置单元格值

可以使用工作表的 setCellValueByColumnAndRow()方法通过坐标设置单元格值。

// Set cell A5 with a string value
$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet');

注意:列引用从1到列A。

按列和行检索单元格值

要检索单元格的值,应首先使用getCellByColumnAndRow()方法从工作表中检索单元格。可以使用以下代码行再次读取单元格的值:

// Get the value from cell B5
$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 5)->getValue();

如果您需要单元格的计算值,请使用以下代码。这在计算引擎中有进一步解释。

// Get the value from cell A4
$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(1, 4)->getCalculatedValue();

将一系列单元格值检索到数组

也可以使用toArray(),rangeToArray()namedRangeToArray()方法在单个调用中将一系列单元格值检索到数组中。

$dataArray = $spreadsheet->getActiveSheet()
    ->rangeToArray(
        'C3:E5',     // The worksheet range that we want to retrieve
        NULL,        // Value that should be returned for empty cells
        TRUE,        // Should formulas be calculated (the equivalent of getCalculatedValue() for each cell)
        TRUE,        // Should values be formatted (the equivalent of getFormattedValue() for each cell)
        TRUE         // Should the array be indexed by cell row and cell column
    );

这些方法都将返回一个由行和列组成的二维数组。该toArray()方法将返回整个工作表;rangeToArray()将返回指定范围或单元格;而namedRangeToArray()将返回定义的named range.

循环遍历单元格

使用迭代器遍历单元格

循环单元格的最简单方法是使用迭代器。使用迭代器,可以使用 foreach 循环工作表、工作表中的行和行中的单元格。

下面是一个示例,我们读取工作表中的所有值并将它们显示在表格中。

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");

$worksheet = $spreadsheet->getActiveSheet();

echo '<table>' . PHP_EOL;
foreach ($worksheet->getRowIterator() as $row) {
    echo '<tr>' . PHP_EOL;
    $cellIterator = $row->getCellIterator();
    $cellIterator->setIterateOnlyExistingCells(FALSE); // This loops through all cells,
                                                       //    even if a cell value is not set.
                                                       // For 'TRUE', we loop through cells
                                                       //    only when their value is set.
                                                       // If this method is not called,
                                                       //    the default value is 'false'.
    foreach ($cellIterator as $cell) {
        echo '<td>' .
             $cell->getValue() .
             '</td>' . PHP_EOL;
    }
    echo '</tr>' . PHP_EOL;
}
echo '</table>' . PHP_EOL;

请注意,我们已将单元格迭代器设置setIterateOnlyExistingCells() 为 FALSE。这使得迭代器循环工作表范围内的所有单元格,即使它们尚未设置。

如果单元格不存在,单元格迭代器将在工作表中创建一个新的空单元格;如果未在工作表中设置,则返回null作为单元格值;尽管我们也可以告诉它返回一个空值而不是返回一个新的空单元格。将单元格迭代器设置setIterateOnlyExistingCells()为false 将循环工作表中当时可用的所有单元格。如果这被设置为在需要时创建新的单元格,那么它可能会增加内存使用量!仅在打算循环所有可能可用的单元格时才使用它;否则,如果单元格不存在,则使用该选项返回空值,或者仅迭代已经存在的单元格。

也可以调用 Row 对象的isEmpty()方法来确定是否需要为该行实例化 Cell Iterator。

使用索引遍历单元格

人们可以使用这种可能性来按列和行索引访问单元格值,例如[1, 1]代替'A1'在循环中读取和写入单元格值。

注意:在 PhpSpreadsheet 中,列索引和行索引是从 1 开始的。也就是说'A1'~[1, 1]

下面是一个示例,我们读取工作表中的所有值并将它们显示在表格中。

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");

$worksheet = $spreadsheet->getActiveSheet();
// Get the highest row and column numbers referenced in the worksheet
$highestRow = $worksheet->getHighestRow(); // e.g. 10
$highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn); // e.g. 5

echo '<table>' . "\n";
for ($row = 1; $row <= $highestRow; ++$row) {
    echo '<tr>' . PHP_EOL;
    for ($col = 1; $col <= $highestColumnIndex; ++$col) {
        $value = $worksheet->getCellByColumnAndRow($col, $row)->getValue();
        echo '<td>' . $value . '</td>' . PHP_EOL;
    }
    echo '</tr>' . PHP_EOL;
}
echo '</table>' . PHP_EOL;

或者,您可以利用 PHP 的“Perl 风格”字符增量器按坐标循环遍历单元格:

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");

$worksheet = $spreadsheet->getActiveSheet();
// Get the highest row number and column letter referenced in the worksheet
$highestRow = $worksheet->getHighestRow(); // e.g. 10
$highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
// Increment the highest column letter
$highestColumn++;

echo '<table>' . "\n";
for ($row = 1; $row <= $highestRow; ++$row) {
    echo '<tr>' . PHP_EOL;
    for ($col = 'A'; $col != $highestColumn; ++$col) {
        echo '<td>' .
             $worksheet->getCell($col . $row)
                 ->getValue() .
             '</td>' . PHP_EOL;
    }
    echo '</tr>' . PHP_EOL;
}
echo '</table>' . PHP_EOL;

请注意,我们不能在此处使用 <= 比较,因为'AA'会匹配为 <= 'B',因此我们递增最高列字母,然后循环递增$col !=最高列。

使用值绑定器来促进数据输入

在内部,PhpSpreadsheet 使用默认 \PhpOffice\PhpSpreadsheet\Cell\IValueBinder实现 (\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder) 来确定使用单元格的setValue()方法输入数据的数据类型(该 setValueExplicit()方法绕过此检查)。

或者,可以修改 PhpSpreadsheet 的默认行为,以便更轻松地输入数据。例如,一个 \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder类是可用的。它自动将百分比、科学格式的数字和以字符串形式输入的日期转换为正确的格式,同时设置单元格的样式信息。以下示例演示如何在 PhpSpreadsheet 中设置值绑定器:

/** PhpSpreadsheet */
require_once 'src/Boostrap.php';

// Set value binder
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() );

// Create new Spreadsheet object
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();

// ...
// Add some data, resembling some different data types
$spreadsheet->getActiveSheet()->setCellValue('A4', 'Percentage value:');
// Converts the string value to 0.1 and sets percentage cell style
$spreadsheet->getActiveSheet()->setCellValue('B4', '10%');

$spreadsheet->getActiveSheet()->setCellValue('A5', 'Date/time value:');
// Converts the string value to an Excel datestamp and sets the date format cell style
$spreadsheet->getActiveSheet()->setCellValue('B5', '21 December 1983');

或者,如果您想将所有内容保留为字符串,则可以使用一个\PhpOffice\PhpSpreadsheet\Cell\StringValueBinder类。如果您要加载的文件包含可以解释为数字的值(例如带有前导符号的数字,例如国际电话号码,+441615579382如保持为字符串)。

默认情况下,StringValueBinder 会将传递给它的任何数据类型转换为字符串。但是,有许多设置允许您指定某些数据类型不应转换为字符串,而是“按原样”保留:

// Set value binder
$stringValueBinder = new \PhpOffice\PhpSpreadsheet\Cell\StringValueBinder();
$stringValueBinder->setNumericConversion(false)
    ->setBooleanConversion(false)
    ->setNullConversion(false)
    ->setFormulaConversion(false);
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( $stringValueBinder );

通过指定要在单元格setValue()或工作表的setCellValue()方法中使用的不同活页夹,您可以在设置单个单元格值时覆盖当前活页夹。

$spreadsheet = new Spreadsheet();
Cell::setValueBinder(new AdvancedValueBinder());

$value = '12.5%';

$cell = $spreadsheet->getActiveSheet()->getCell('A1');
// Value will be set as a number 0.125 with a format mask '0.00%'
$cell->setValue($value); // Using the Advanced Value Binder

$cell = $spreadsheet->getActiveSheet()->getCell('A2');
// Value will be set as a string '12.5%' with a format mask 'General'
$cell->setValue($value, new StringValueBinder()); // Overriding the Advanced Value Binder

创建自己的价值绑定器

创建您自己的价值绑定器相对简单。当需要更专业的值绑定时,您可以实现 \PhpOffice\PhpSpreadsheet\Cell\IValueBinder接口或扩展现有的 \PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder类。

原创文章,作者:平凡老赵,未经允许不得转载,出处:https://www.pfjsb.com/kfyw/composer-phpoffice-phpspreadsheet.html
-- 展开阅读全文 --
Markdown语法最全汇总精心整理
« 上一篇 04-21
PHP生成pdf文件的几个composer包
下一篇 » 04-23

热门文章

标签TAG