一般讀取檔案我們用fopen 或者 file_get_contents ,前者可以迴圈讀取,後者可以一次性讀取,但都是將檔案內容一次性載入來操作。如果載入的檔案特別大時,如幾百M,上G時,這時效能就降下來了,那麼PHP裡有沒有對大檔案的處理函數或者類呢? 答案是:有的。
PHP真的越來越“物件導向”了,一些原有的基礎的SPL方法都開始陸續地實現出class了。
從 PHP 5.1.0 開始,SPL 庫增加了 SplFileObject 與 SplFileInfo 兩個標準的檔案操作類。SplFileInfo 是從 PHP 5.1.2 開始實現的。
從字面意思理解看,可以看出 SplFileObject 要比 SplFileInfo 更為強大。
不錯,SplFileInfo 僅用於擷取檔案的一些屬性資訊,如檔案大小、檔案訪問時間、檔案修改時間、尾碼名等值,而 SplFileObject 是繼承 SplFileInfo 這些功能的。
| 代碼如下 |
複製代碼 |
/** 返迴文件從X行到Y行的內容(支援php5、php4) * @param string $filename 檔案名稱 * @param int $startLine 開始的行數 * @param int $endLine 結束的行數 * @return string */ function getFileLines($filename, $startLine = 1, $endLine=50, $method='rb') { $content = array(); $count = $endLine - $startLine; // 判斷php版本(因為要用到SplFileObject,PHP>=5.1.0) if(version_compare(PHP_VERSION, '5.1.0', '>=')){ $fp = new SplFileObject($filename, $method); $fp->seek($startLine-1);// 轉到第N行, seek方法參數從0開始計數 for($i = 0; $i <= $count; ++$i) { $content[]=$fp->current();// current()擷取當前行內容 $fp->next();// 下一行 } }else{//PHP<5.1 $fp = fopen($filename, $method); if(!$fp) return 'error:can not read file'; for ($i=1;$i<$startLine;++$i) {// 跳過前$startLine行 fgets($fp); } for($i;$i<=$endLine;++$i){ $content[]=fgets($fp);// 讀取檔案行內容 } fclose($fp); } return array_filter($content); // array_filter過濾:false,null,'' } |
Ps: 上面都沒加”讀取到末尾的判斷”:!$fp->eof() 或者 !feof($fp),加上這個判斷影響效率,自己加上測試很多很多很多行的已耗用時間就曉得了,而且這裡加上也完全沒必要。
從上面的函數就可以看出來使用SplFileObject比下面的fgets要快多了,特別是檔案行數非常多、並且要取後面的內容的時候。fgets要兩個迴圈才可以,並且要迴圈$endLine次。
此方法花了不少功夫,測試了很多中寫法,就是想得出效率最高的方法。哪位覺得有值得改進的歡迎賜教。
使用,返回35270行-35280行的內容:
| 代碼如下 |
複製代碼 |
echo '<pre>'; var_dump(getFileLines('test.php',35270,35280)); echo '</pre>'; |
再看一個執行個體
| 代碼如下 |
複製代碼 |
function readBigFile($filename, $count = 20, $tag = "rn") { $content = "";//最終內容 $current = "";//當前讀取內容寄存 $step= 1;//每次走多少字元 $tagLen = strlen($tag); $start = 0;//起始位置 $i = 0;//計數器 $handle = fopen($filename,'r+');//讀寫入模式開啟檔案,指標指向檔案起始位置 while($i < $count && !feof($handle)) { fseek($handle, $start, SEEK_SET);//指標設定在檔案開頭 $current = fread($handle,$step);//讀取檔案 $content .= $current;//組合字元串 $start += $step;//依據步長向前移動 //依據分隔字元的長度截取字串最後免得幾個字元 $substrTag = substr($content, -$tagLen); if ($substrTag == $tag) { //判斷是否為判斷是否是換行或其他分隔字元 $i++; $content .= "<br />"; } } //關閉檔案 fclose($handle); //返回結果 return $content; } $filename = "csdn.sql";//需要讀取的檔案 $tag = "n";//行分隔字元 注意這裡必須用雙引號 $count = 100;//讀取行數 $data = readBigFile($filename,$count,$tag); echo $data; |
注意:通過使用PHP的fseek和fread相結合,即可做到隨意讀取檔案中的某一部份資料,關於函數傳入的變數$tag的值,根據系統不一樣,傳入的值也是有區別的:Windows用”rn”,linux/unix用”n”,Mac OS用”r”。