您當前的位置:首頁 > 舞蹈

用PHP讀取Excel、CSV檔案

作者:由 計算機科學 發表于 舞蹈時間:2019-01-01

用PHP讀取Excel、CSV檔案

PhpSpreadsheet

PHP讀取excel、csv檔案的庫有很多,但用的比較多的有: PHPOffice/PHPExcel、PHPOffice/PhpSpreadsheet,現在PHPExcel已經不再維護了,最新的一次提交還是在2017年12月25號,建議直接使用PhpSpreadsheet,而且這兩個專案都是同一個組織維護的,本文介紹PhpSpreadsheet的使用。

介紹PhpSpreadsheet

PhpSpreadsheet這個庫是純PHP寫的,提供了非常豐富的類和方法,而且支援很多檔案格式:

用PHP讀取Excel、CSV檔案

PhpSpreadsheet支援檔案格式

環境要求

PHP >= 5。6

開啟`php_zip`擴充套件

開啟`php_xml`擴充套件

開啟`php_gd2`擴充套件

開始使用

我們寫一個簡單的demo,來學習PhpSpreadsheet的使用,大概就是一個簡單的檔案上傳頁面,上傳我們要讀取的Excel檔案,PHP接收到檔案,呼叫PhpSpreadsheet讀取Excel裡面的內容。

0. 配置環境

略。。。,自己配置

我當前的PHP版本是7。2。13

1. 新建一個專案

mkdir demo

cd

demo

2. 安裝

使用Composer安裝:

composer require phpoffice/phpspreadsheet

預設安裝的是最新的穩定版本(1。5),如果想要安裝dev版本,可以執行下面的命令:

composer require phpoffice/phpspreadsheet:develop

上面步驟執行完畢後,目錄結構是這樣的:

用PHP讀取Excel、CSV檔案

3. 新建一個簡單的html檔案,用來上傳Excel檔案

vim index。html

index。html裡面的內容很簡單,如下:

用PHP讀取Excel、CSV檔案

這裡要注意下:form表單的enctype一定要是multipart/form-data

這只是一個簡單的demo,一個form表單就可以了,執行後就是下面這樣了 :)

用PHP讀取Excel、CSV檔案

4. PhpSpreadsheet如何使用?

在處理前端傳過來的Excel檔案之前,先來介紹下PhpSpredsheet如何使用。

4.1 讀取檔案

PhpSpreadsheet中讀取檔案有很多種,對於不同格式的檔案有不同的讀取方法,比如:xlsx格式,使用\PhpOffice\PhpSpreadsheet\Reader\Xlsx(),csv格式,使用\PhpOffice\PhpSpreadsheet\Reader\Csv(),乍一看這麼多類就感覺有點複雜,其實這些類都實現了\PhpOffice\PhpSpreadsheet\Reader\IReader和\PhpOffice\PhpSpreadsheet\Writer\IWriter介面,指定了要載入的檔案型別。我們可以直接使用\PhpOffice\PhpSpreadsheet\IOFactory這個工廠類:

$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load(‘demo。xlsx’);

如果想在讀寫檔案的時候設定一些屬性,比如讀寫屬性,可以這樣設定:

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile(“demo。xlsx”);

$reader->setReadDataOnly(true);

$reader->load(“demo。xlsx”);

使用這個工廠類的好處就是你不需要關心檔案上傳的格式,它能自動幫識別,其實這個工廠類就是對你上傳的檔案做一些識別,如果識別出來是xls格式,就返回xls的reader,如果是csv,就返回csv的reader,透過分析程式碼我們可以看到這個IOFactory可以生產出如下的reader和writer:

abstract class IOFactory

{

private static $readers = [

‘Xlsx’ => Reader\Xlsx::class,

‘Xls’ => Reader\Xls::class,

‘Xml’ => Reader\Xml::class,

‘Ods’ => Reader\Ods::class,

‘Slk’ => Reader\Slk::class,

‘Gnumeric’ => Reader\Gnumeric::class,

‘Html’ => Reader\Html::class,

‘Csv’ => Reader\Csv::class,

];

private static $writers = [

‘Xls’ => Writer\Xls::class,

‘Xlsx’ => Writer\Xlsx::class,

‘Ods’ => Writer\Ods::class,

‘Csv’ => Writer\Csv::class,

‘Html’ => Writer\Html::class,

‘Tcpdf’ => Writer\Pdf\Tcpdf::class,

‘Dompdf’ => Writer\Pdf\Dompdf::class,

‘Mpdf’ => Writer\Pdf\Mpdf::class,

];

。。。

可以看到支援的型別還是蠻多的,但是很多都不常用。

在IOFactory工廠中還可以指定讀寫的檔案型別,返回對應的reader,這樣就免去了識別檔案型別的步驟,如下:

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader(“Xlsx”); // 指定為xlsx格式

$spreadsheet = $reader->load(“demo。xlsx”);

4.2 從原始碼比較兩種讀寫方式

首先,來看下IOFactory這個工廠類,我們在不指定reader型別時直接load,程式碼內部是要做一個識別格式的操作:

// 原始碼解析

// 不指定reader,直接獲取上傳的檔案建立

$reader = \PhpOffice\PhpSpreadsheet\IOFactory::load($_FILES[‘file’][‘tmp_name’]);

// IOFactory::load()

public static function load($pFilename)

{

// 這步棋室就是建立reader,免去了你手動建立

$reader = self::createReaderForFile($pFilename);

return $reader->load($pFilename);

}

// IOFactory::createReaderForFile()

// 這步就是返回一個reader,具體返回什麼reader,是根據檔名來的

public static function createReaderForFile($filename)

{

// 判斷檔案是否存在並且可讀,會丟擲InvalidArgumentException

File::assertFile($filename);

// 根據檔案字尾猜測型別

$guessedReader = self::getReaderTypeFromExtension($filename);

if ($guessedReader !== null) {

$reader = self::createReader($guessedReader);

// Let‘s see if we are lucky

if (isset($reader) && $reader->canRead($filename)) {

return $reader;

}

}

// 如果沒有檢測到型別,就會遍歷預設的reader陣列,直到找到可以使用的那個reader

foreach (self::$readers as $type => $class) {

if ($type !== $guessedReader) {

$reader = self::createReader($type);

if ($reader->canRead($filename)) {

return $reader;

}

}

}

throw new Reader\Exception(’Unable to identify a reader for this file‘);

}

從上面的程式碼,可以看到在load前是做了檔案檢測和型別判斷的操作,然後再返回對應的reader,接下來,再來看看當我們指定了型別後,做了哪些操作的:

// 指定reader

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();

$spreadsheet = $reader->load($_FILES[’file‘][’tmp_name‘]);

上面的就比較簡單了,直接建立reader,然後就load了,只是做了一些例項化的操作。這兩種方法相比,第二種方法效能更好一點,當然前提是要知道檔案格式。

5. 讀取Excel檔案內容

讓我們接著繼續上面的index。html,我們需要編寫一個PHP檔案來處理請求:

require ’vendor/autoload。php‘;

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();

try {

$spreadsheet = $reader->load($_FILES[’file‘][’tmp_name‘]);

} catch (\PhpOffice\PhpSpreadsheet\Reader\Exception $e) {

die($e->getMessage());

}

$sheet = $spreadsheet->getActiveSheet();

$res = array();

foreach ($sheet->getRowIterator(2) as $row) {

$tmp = array();

foreach ($row->getCellIterator() as $cell) {

$tmp[] = $cell->getFormattedValue();

}

$res[$row->getRowIndex()] = $tmp;

}

echo json_encode($res);

我們先引入autoload,接著建立了一個Xlsx的reader,然後load我們上傳的檔案,因為在excel中,內容都是按sheet區分的,每一個sheet中都由行和列組成,我們獲取到當前使用的sheet,透過sheet獲取到行的迭代物件,再針對每一行得到每一列物件,在PhpSpreadsheet中,cell是一個最小的單元,對應著第幾行第幾列,資料都是存在cell中,得到cell物件我們就能獲取到資料。

當我們上傳如下內容後:

用PHP讀取Excel、CSV檔案

返回結果如下:

用PHP讀取Excel、CSV檔案

因為我們在讀取時,是從第二行開始的,所以第一行的內容就不顯示了。

這裡說一下,在Excel中第三列是一個時間,PhpSpreadsheet對時間的處理有點特殊。在PhpSpreadsheet中date和time在儲存時都是作為數字型別,當要區分數字是時間格式時,需要用到format mask,預設情況下,format mask是開啟了的,但如果設定setReadDataOnly等於true的話,就不能使用format mask,從而就區分不了數字和時間格式,PhpSpreatsheet將會全部作為數字處理。

此時,我們開啟只讀模式看一下,

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();

$reader->setReadDataOnly(true);

輸出結果如下:

用PHP讀取Excel、CSV檔案

第三列就變成了奇怪的數字,當初這個問題還困擾了我半天。

5. PhpSpreadsheet讀取檔案時的一些常用方法

1。 如果一個Excel中有多個sheet,只想操作其中的某幾個sheet,可以設定setLoadSheetsOnly

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();

// 引數支援字串或一個數組

$reader->setLoadSheetsOnly([’sheet1‘,’sheet3‘]);

2。 讀取指定行和列的資料

class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter

{

public function readCell($column, $row, $worksheetName = ’‘) {

// 只讀取A1:E7的資料

if ($row >= 1 && $row <= 7) {

if (in_array($column,range(’A‘,’E‘))) {

return true;

}

}

return false;

}

}

$myFilter = new MyReadFilter();

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();

$reader->setReadFilter($filterSubset);

$spreadsheet = $reader->load(’demo。xlsx‘);

上面的例子不夠通用,可以修改下使之更為通用:

class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter

{

private $startRow = 0;

private $endRow = 0;

private $columns = [];

public function __construct($startRow, $endRow, $columns) {

$this->startRow = $startRow;

$this->endRow = $endRow;

$this->columns = $columns;

}

public function readCell($column, $row, $worksheetName = ’‘) {

if ($row >= $this->startRow && $row <= $this->endRow) {

if (in_array($column,$this->columns)) {

return true;

}

}

return false;

}

}

$myFilter = new MyReadFilter(9,15,[’A‘, ’B‘, ’D‘]);

3。 列出Excel中所有sheet的名字

$reader->listWorksheetNames(’demo。xlsx‘);

4。 列出一個sheet的資訊,包括多少列、多少行

$reader->listWorksheetInfo(’demo。xlsx‘);

PhpSpreadsheet的學習與使用就到這,真的很強大,幾乎滿足了日常的所有需求,是讀取Excel、CSV檔案的利器。