您好,登錄后才能下訂單哦!
這篇文章主要講解了“PHP中Swoole多進程讀取大文件示例”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“PHP中Swoole多進程讀取大文件示例”吧!
PHP讀取大文件源碼示例,通過PHP讀取過大、超大型文件的思路及解決方案。
在日常讀取文件時,若文件 不是很大,通常使用file_get_contents,將內(nèi)容一次性載入的變量中,也可以遠程加載網(wǎng)頁或者遠端文件。
若加載超過PHP限制的內(nèi)存大小,或者超過本機內(nèi)存大小的文件進程就會報錯或者崩掉。
為了解決這個問題,我們采用使用完畢并釋放的原則來讀取大文件。
如果不考慮多線程的情況下,單線程讀取大文件采用while fread就可以實現(xiàn)。
如下代碼
$handle = fopen("./big.txt", "rb"); while (!feof($handle)) { $contents = fread($handle, 8192); // 業(yè)務處理 unset($contents); // 釋放掉變量 } fclose($handle);
feof是判斷是否到文件尾,如果沒有到文件尾,則會一直while循環(huán),并執(zhí)行讀取操作。每次讀取8192字節(jié),然后使用過后將其釋放掉。
往往很多文件并不是一行的,有多行內(nèi)容。需要將每行內(nèi)容讀取出來當做一條數(shù)據(jù)處理,也可以使用fgets。
即如下代碼:
$handle = fopen("./big.txt", "rb"); while (!feof($handle)) { $contents = fgets($handle, 1024); // 業(yè)務處理 unset($contents); // 釋放掉變量 } fclose($handle);
這里的fgets第二個參數(shù),默認為1024字節(jié)。即默認讀取一行數(shù)據(jù),如果一行數(shù)據(jù)小于1024字節(jié),則完整讀取。如果超出1024字節(jié),則只取前1024字節(jié)。遇到換行符"\n"或者"\r\n"或者結束符會停止讀取。
如果遇到變態(tài)的文件,很多行都只有1000長度,某一行有8000長度,如果在不清楚的情況下,就很難掌控,若要完整讀取就需要指定讀取的最大字節(jié),8000才能將每一行完整讀取。
還有一種方法就是自己處理換行符。默認讀取1024字節(jié),然后放置到內(nèi)存中,使用過后再將其釋放。這種操作很節(jié)省內(nèi)存,但是在邏輯處理上需要自己處理換行符。
如,讀取1024字節(jié),沒有換行符,則保存數(shù)據(jù)繼續(xù)讀取。再讀取1024字節(jié),判斷其中是否有換行符,如果有則處理最開始到換行符中的數(shù)據(jù)。再將剩下的數(shù)據(jù)保存,等待下一次讀取,直到整個文件讀取完畢。
$handle = fopen("./big.txt", "rb"); $contents = ""; while (!feof($handle)) { $contents .= fread($handle, 8192); // 判斷讀取到的內(nèi)容是否包含換行符,包含則進入循環(huán)體 while(strpos($contents, "\n") !== false){ $eol_pos= strpos($contents, "\n"); $line = substr($contents, 0, $eol_pos); // $line為一行的數(shù)據(jù),進行業(yè)務處理,并釋放 unset($line); $contents = substr($eol_pos, 0);// 將剩余內(nèi)容放置到變量中以供下次使用 } } fclose($handle);
PHP默認沒有多線程,這里可以采用多進程的方式實現(xiàn),或者swoole的多進程來實現(xiàn)。
例如讀取一個8GB文件,分8個線程,每個線程讀取1GB數(shù)據(jù)內(nèi)容。或者更多線程進行拆分工作內(nèi)容。
首先第一步,就是獲取整個文件的體積大小,然后計算每個線程應該負責處理的一部分內(nèi)容。
function length($filename) { $handle = fopen($filename, "rb"); $currentPos = ftell($handle); fseek($handle, 0, SEEK_END); $length = ftell($handle); fseek($handle, $currentPos); // $length 文件總長度 return $length; } echo length("./big.txt");
這里主要說明下作了哪些內(nèi)容,首先是設定分配總的線程數(shù)。然后根據(jù)設置的線程數(shù),計算每個線程要讀取的數(shù)據(jù)大小,即從哪里開始讀,讀到哪里結束。
然后必定會出現(xiàn)拆分后,讀到不完整行的情況,在這里來解決這種前半行或者后半行的意外情況。
解決邏輯就是,假設線程開始的讀取位置在某一行的中間,我們一個字符向前移動,移動到上個換行符(也可能是最開始)即可獲取到整行文本內(nèi)容。
處理掉殘行數(shù)據(jù)之后,使用yield來傳遞數(shù)據(jù)給業(yè)務處理。
$filename = "./big.txt"; $maxProcess = 8;// 分配8個線程 $length = length($filename); $singleProcessLength = ceil($length / $maxProcess); // 線程負責讀取的內(nèi)容 function processRead($filename, $index, $singleProcessLength) { $fh = fopen($filename, 'r'); $beginPos = $index * $singleProcessLength; //結束位置=線程序列*線程處理數(shù)據(jù)長度+線程處理數(shù)據(jù) - 1 (長度轉指針,實際結束指針小于結束長度) $endPos = $index * $singleProcessLength + $singleProcessLength - 1; fseek($fh, $beginPos); echo '線程:' . $index . ',起始位置:' . $beginPos . ',結束位置:' . $endPos . PHP_EOL; //移動到上個\n 以便首次順利獲取整行內(nèi)容 while (fseek($fh, -1, SEEK_CUR) === 0) { if (fread($fh, 1) == "\n" || ftell($fh) <= 0) { break; } fseek($fh, -1, SEEK_CUR); } echo '線程:' . $index . ',移動完畢!!!!!' . PHP_EOL; //整行讀取數(shù)據(jù) //結束時位置超過預計結束位置是正常狀況,fgets 讀取一整行內(nèi)容 //預計結束位置可能在行內(nèi),所以產(chǎn)生不同結果。 while (ftell($fh) <= $endPos && !feof($fh)) { yield $raw = fgets($fh); } echo '進程' . $index . '結束時 指針位置:' . ftell($fh) . ', 應該到:' . $endPos . PHP_EOL; fclose($fh); } foreach(range(0,$maxProcess - 1) as $index){ // 多線程采用多線程的方式創(chuàng)建,這里采用yield回調(diào)。 foreach(processRead($filename, $index, $singleProcessLength) as $value){ // $value為每一行的內(nèi)容,處理后釋放 unset($value); } }
感謝各位的閱讀,以上就是“PHP中Swoole多進程讀取大文件示例”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對PHP中Swoole多進程讀取大文件示例這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。